Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Notifications: fixes, cleanup, and tests (#131)
* notifications: fixes, cleanup, and tests * add notification tests for _init_nsapp and _default_user_notification_center * add to Notification class with pythonic interpretation of objc notification object * log changes for notifications
- Loading branch information
Showing
11 changed files
with
553 additions
and
210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
from __future__ import print_function | ||
|
||
import inspect | ||
import traceback | ||
|
||
import Foundation | ||
|
||
from . import compat | ||
from . import exceptions | ||
|
||
|
||
def require_string(*objs): | ||
for obj in objs: | ||
if not isinstance(obj, compat.string_types): | ||
raise TypeError( | ||
'a string is required but given {0}, a {1}'.format(obj, type(obj).__name__) | ||
) | ||
|
||
|
||
def require_string_or_none(*objs): | ||
for obj in objs: | ||
if not(obj is None or isinstance(obj, compat.string_types)): | ||
raise TypeError( | ||
'a string or None is required but given {0}, a {1}'.format(obj, type(obj).__name__) | ||
) | ||
|
||
|
||
def call_as_function_or_method(func, event): | ||
# The idea here is that when using decorators in a class, the functions passed are not bound so we have to | ||
# determine later if the functions we have (those saved as callbacks) for particular events need to be passed | ||
# 'self'. | ||
# | ||
# This works for an App subclass method or a standalone decorated function. Will attempt to find function as | ||
# a bound method of the App instance. If it is found, use it, otherwise simply call function. | ||
from . import rumps | ||
try: | ||
app = getattr(rumps.App, '*app_instance') | ||
except AttributeError: | ||
pass | ||
else: | ||
for name, method in inspect.getmembers(app, predicate=inspect.ismethod): | ||
if method.__func__ is func: | ||
return method(event) | ||
return func(event) | ||
|
||
|
||
def guard_unexpected_errors(func): | ||
"""Decorator to be used in PyObjC callbacks where an error bubbling up | ||
would cause a crash. Instead of crashing, print the error to stderr and | ||
prevent passing to PyObjC layer. | ||
For Python 3, print the exception using chaining. Accomplished by setting | ||
the cause of :exc:`rumps.exceptions.InternalRumpsError` to the exception. | ||
For Python 2, emulate exception chaining by printing the original exception | ||
followed by :exc:`rumps.exceptions.InternalRumpsError`. | ||
""" | ||
def wrapper(*args, **kwargs): | ||
try: | ||
return func(*args, **kwargs) | ||
|
||
except Exception as e: | ||
internal_error = exceptions.InternalRumpsError( | ||
'an unexpected error occurred within an internal callback' | ||
) | ||
if compat.PY2: | ||
import sys | ||
traceback.print_exc() | ||
print('\nThe above exception was the direct cause of the following exception:\n', file=sys.stderr) | ||
traceback.print_exception(exceptions.InternalRumpsError, internal_error, None) | ||
else: | ||
internal_error.__cause__ = e | ||
traceback.print_exception(exceptions.InternalRumpsError, internal_error, None) | ||
|
||
return wrapper | ||
|
||
|
||
def string_to_objc(x): | ||
if isinstance(x, compat.binary_type): | ||
return Foundation.NSData.alloc().initWithData_(x) | ||
elif isinstance(x, compat.string_types): | ||
return Foundation.NSString.alloc().initWithString_(x) | ||
else: | ||
raise TypeError( | ||
"expected a string or a bytes-like object but provided %s, " | ||
"having type '%s'" % ( | ||
x, | ||
type(x).__name__ | ||
) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
|
||
class RumpsError(Exception): | ||
"""A generic rumps error occurred.""" | ||
|
||
|
||
class InternalRumpsError(RumpsError): | ||
"""Internal mechanism powering functionality of rumps failed.""" |
Oops, something went wrong.