more refactoring and unittest completion

This commit is contained in:
Chris Caron 2017-12-03 15:44:39 -05:00
parent 9c4502a18b
commit a65a8a8373
5 changed files with 141 additions and 90 deletions

View File

@ -1,10 +1,3 @@
dist: trusty
sudo: false
cache:
directories:
- $HOME/.cache/pip
language: python language: python

View File

@ -28,6 +28,7 @@ from .utils import compat_is_basestring
from .AppriseAsset import AppriseAsset from .AppriseAsset import AppriseAsset
from . import NotifyBase
from . import plugins from . import plugins
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -111,6 +112,70 @@ class Apprise(object):
if servers: if servers:
self.add(servers) self.add(servers)
@staticmethod
def instantiate(url, asset=None, suppress_exceptions=True):
"""
Returns the instance of a instantiated plugin based on the provided
Server URL. If the url fails to be parsed, then None is returned.
"""
# swap hash (#) tag values with their html version
# This is useful for accepting channels (as arguments to pushbullet)
_url = url.replace('/#', '/%23')
# Attempt to acquire the schema at the very least to allow our plugins
# to determine if they can make a better interpretation of a URL
# geared for them anyway.
schema = GET_SCHEMA_RE.match(_url)
if schema is None:
logger.error('%s is an unparseable server url.' % url)
return None
# Update the schema
schema = schema.group('schema').lower()
# Some basic validation
if schema not in SCHEMA_MAP:
logger.error(
'{0} is not a supported server type (url={1}).'.format(
schema,
_url,
)
)
return None
# Parse our url details
# the server object is a dictionary containing all of the information
# parsed from our URL
results = SCHEMA_MAP[schema].parse_url(_url)
if not results:
# Failed to parse the server URL
logger.error('Could not parse URL: %s' % url)
return None
if suppress_exceptions:
try:
# Attempt to create an instance of our plugin using the parsed
# URL information
plugin = SCHEMA_MAP[results['schema']](**results)
except:
# the arguments are invalid or can not be used.
logger.error('Could not load URL: %s' % url)
return None
else:
# Attempt to create an instance of our plugin using the parsed
# URL information but don't wrap it in a try catch
plugin = SCHEMA_MAP[results['schema']](**results)
# Save our asset
if asset:
plugin.asset = asset
return plugin
def add(self, servers, asset=None): def add(self, servers, asset=None):
""" """
Adds one or more server URLs into our list. Adds one or more server URLs into our list.
@ -120,66 +185,27 @@ class Apprise(object):
# Initialize our return status # Initialize our return status
return_status = True return_status = True
if asset is None:
# prepare default asset
asset = self.asset
if isinstance(servers, NotifyBase):
# Go ahead and just add our plugin into our list
self.servers.append(servers)
return True
servers = parse_list(servers) servers = parse_list(servers)
for _server in servers: for _server in servers:
# swap hash (#) tag values with their html version # Instantiate ourselves an object, this function throws or
# This is useful for accepting channels (as arguments to # returns None if it fails
# pushbullet) instance = Apprise.instantiate(_server, asset=asset)
_server = _server.replace('/#', '/%23') if not instance:
# Attempt to acquire the schema at the very least to allow
# our plugins to determine if they can make a better
# interpretation of a URL geared for them anyway.
schema = GET_SCHEMA_RE.match(_server)
if schema is None:
logger.error(
'%s is an unparseable server url.' % _server,
)
return_status = False return_status = False
continue continue
# Update the schema
schema = schema.group('schema').lower()
# Some basic validation
if schema not in SCHEMA_MAP:
logger.error(
'%s is not a supported server type.' % schema,
)
return_status = False
continue
# Parse our url details
# the server object is a dictionary containing all of the
# information parsed from our URL
results = SCHEMA_MAP[schema].parse_url(_server)
if not results:
# Failed to parse the server URL
logger.error('Could not parse URL: %s' % _server)
return_status = False
continue
try:
# Attempt to create an instance of our plugin using the parsed
# URL information
plugin = SCHEMA_MAP[results['schema']](**results)
except:
# the arguments are invalid or can not be used.
return_status = False
continue
# Save our asset
if asset:
plugin.asset = asset
else:
plugin.asset = self.asset
# Add our initialized plugin to our server listings # Add our initialized plugin to our server listings
self.servers.append(plugin) self.servers.append(instance)
# Return our status # Return our status
return return_status return return_status

View File

@ -27,6 +27,7 @@ from .common import NOTIFY_TYPES
from .common import NOTIFY_IMAGE_SIZES from .common import NOTIFY_IMAGE_SIZES
from .common import NotifyImageSize from .common import NotifyImageSize
from .plugins.NotifyBase import NotifyFormat from .plugins.NotifyBase import NotifyFormat
from .plugins.NotifyBase import NotifyBase
from .Apprise import Apprise from .Apprise import Apprise
from .AppriseAsset import AppriseAsset from .AppriseAsset import AppriseAsset
@ -38,7 +39,7 @@ logging.getLogger(__name__).addHandler(NullHandler())
__all__ = [ __all__ = [
# Core # Core
'Apprise', 'AppriseAsset', 'Apprise', 'AppriseAsset', 'NotifyBase',
# Reference # Reference
'NotifyType', 'NotifyImageSize', 'NotifyFormat', 'NOTIFY_TYPES', 'NotifyType', 'NotifyImageSize', 'NotifyFormat', 'NOTIFY_TYPES',

View File

@ -38,27 +38,16 @@ from ..common import NOTIFY_TYPES
from ..AppriseAsset import AppriseAsset from ..AppriseAsset import AppriseAsset
# Define a general HTML Escaping # use sax first because it's faster
try: from xml.sax.saxutils import escape as sax_escape
# use sax first because it's faster
from xml.sax.saxutils import escape as sax_escape
def _escape(text):
def _escape(text):
""" """
saxutil escape tool saxutil escape tool
""" """
return sax_escape(text, {"'": "'", "\"": """}) return sax_escape(text, {"'": "'", "\"": """})
except ImportError:
# if we can't, then fall back to cgi escape
from cgi import escape as cgi_escape
def _escape(text):
"""
cgi escape tool
"""
return cgi_escape(text, quote=True)
HTTP_ERROR_MAP = { HTTP_ERROR_MAP = {
400: 'Bad Request - Unsupported Parameters.', 400: 'Bad Request - Unsupported Parameters.',
@ -344,7 +333,6 @@ class NotifyBase(object):
# Support SSL Certificate 'verify' keyword. Default to being enabled # Support SSL Certificate 'verify' keyword. Default to being enabled
results['verify'] = True results['verify'] = True
if 'qsd' in results:
if 'verify' in results['qsd']: if 'verify' in results['qsd']:
results['verify'] = parse_bool( results['verify'] = parse_bool(
results['qsd'].get('verify', True)) results['qsd'].get('verify', True))

View File

@ -23,9 +23,10 @@ from os.path import dirname
from apprise import Apprise from apprise import Apprise
from apprise import AppriseAsset from apprise import AppriseAsset
from apprise.Apprise import SCHEMA_MAP from apprise.Apprise import SCHEMA_MAP
from apprise.plugins.NotifyBase import NotifyBase from apprise import NotifyBase
from apprise import NotifyType from apprise import NotifyType
from apprise import NotifyImageSize from apprise import NotifyImageSize
from apprise.Apprise import __load_matrix
def test_apprise(): def test_apprise():
@ -33,6 +34,11 @@ def test_apprise():
API: Apprise() object API: Apprise() object
""" """
# Caling load matix a second time which is an internal function causes it
# to skip over content already loaded into our matrix and thefore accesses
# other if/else parts of the code that aren't otherwise called
__load_matrix()
a = Apprise() a = Apprise()
# no items # no items
@ -165,12 +171,49 @@ def test_apprise():
# simply returns False # simply returns False
assert(a.notify(title="present", body="present") is False) assert(a.notify(title="present", body="present") is False)
# Test instantiating a plugin
class ThrowInstantiateNotification(NotifyBase):
def __init__(self, **kwargs):
# Pretend everything is okay
raise TypeError()
SCHEMA_MAP['throw'] = ThrowInstantiateNotification
# Reset our object
a.clear()
assert(len(a) == 0)
# Instantiate a good object
plugin = a.instantiate('good://localhost')
assert(isinstance(plugin, NotifyBase))
# We an add already substatiated instances into our Apprise object
a.add(plugin)
assert(len(a) == 1)
# Reset our object again
a.clear()
try:
a.instantiate('throw://localhost', suppress_exceptions=False)
assert(False)
except TypeError:
assert(True)
assert(len(a) == 0)
assert(a.instantiate(
'throw://localhost', suppress_exceptions=True) is None)
assert(len(a) == 0)
def test_apprise_asset(tmpdir): def test_apprise_asset(tmpdir):
""" """
API: AppriseAsset() object API: AppriseAsset() object
""" """
a = AppriseAsset(theme=None)
# Default theme
assert(a.theme == 'default')
a = AppriseAsset( a = AppriseAsset(
theme='dark', theme='dark',
image_path_mask='/{THEME}/{TYPE}-{XY}.png', image_path_mask='/{THEME}/{TYPE}-{XY}.png',