How get the list of errors thrown by a function?
When the Swift docs says a function throws
, they mean that it throws an ErrorType
(in Cocoa APIs usually an NSError
), not an exception.
Consider the following do-try-catch
flow for NSFileManager
's createDirectoryAtPath
:
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
do {
try NSFileManager.defaultManager().createDirectoryAtPath(documentsPath, withIntermediateDirectories: false, attributes: nil)
} catch {
// 'error' variable automatically populated
print(error)
print(error.dynamicType)
}
createDirectoryAtPath
will fail because the documents directory already exists. Logging the dynamicType
of the error
shows that it is in fact an NSError
object:
Error Domain=NSCocoaErrorDomain Code=516 "The file “Documents” couldn’t be saved in the folder “35B0B3BF-D502-4BA0-A991-D07568AB87C6” because a file with the same name already exists." UserInfo={NSFilePath=/Users/jal/Library/Developer/CoreSimulator/Devices/E8A35774-C9B7-42F0-93F1-8103FBBC7118/data/Containers/Data/Application/35B0B3BF-D502-4BA0-A991-D07568AB87C6/Documents, NSUnderlyingError=0x7fa88bd14410 {Error Domain=NSPOSIXErrorDomain Code=17 "File exists"}}
NSError
In order to see the different types of errors a function can throw
, you would have to examine the error
for information to determine the type of error thrown, and how to handle each error. In the case of NSError
this would be its domain, code, and description.
In this particular case, a directory already exists at that path, so the file manager cannot create a new directory. An example of another reason why this operation could fail would be if the file manager did not have write access. That would be error code 256.
How Can I Find a List of All Exceptions That a Given Library Function Throws in Python?
To amplify Messa, catch what you expect are failure modes that you know how to recover from. Ian Bicking wrote an article that addresses some of the overarching principles as does Eli Bendersky's note.
The problem with the sample code is that it is not handling errors, just prettifying them and discarding them. Your code does not "know" what to do with a NameError and there isn't much it should do other than pass it up, look at Bicking's re-raise if you feel you must add detail.
IOError and OSError are reasonably "expectable" for a shutil.move
but not necessarily handleable. And the caller of your function wanted it to move a file and may itself break if that "contract" that Eli writes of is broken.
Catch what you can fix, adorn and re-raise what you expect but can't fix, and let the caller deal with what you didn't expect, even if the code that "deals" is seven levels up the stack in main
.
How to list all exceptions a function could raise in Python 3?
You can't get reliable results for some (if not most) functions. Some examples:
functions that execute arbitrary code (e.g.
exec(')(rorrEeulaV esiar'[::-1])
raisesValueError
)functions that aren't written in Python
functions that call other functions that can propagate errors to the caller
functions re-raising active exceptions in the
except:
block
Unfortunately, this list is incomplete.
E.g. os.makedirs
is written in Python and you can see its source:
...
try:
mkdir(name, mode)
except OSError as e:
if not exist_ok or e.errno != errno.EEXIST or not path.isdir(name):
raise
Bare raise
re-raises the last active exception (OSError
or one of its subclasses). Here's the class hierarchy for OSError
:
+-- OSError
| +-- BlockingIOError
| +-- ChildProcessError
| +-- ConnectionError
| | +-- BrokenPipeError
| | +-- ConnectionAbortedError
| | +-- ConnectionRefusedError
| | +-- ConnectionResetError
| +-- FileExistsError
| +-- FileNotFoundError
| +-- InterruptedError
| +-- IsADirectoryError
| +-- NotADirectoryError
| +-- PermissionError
| +-- ProcessLookupError
| +-- TimeoutError
To get the exact exception types you'll need to look into mkdir
, functions it calls, functions those functions call etc.
So, getting possible exceptions without running the function is very hard and you really should not do it.
However for simple cases like
raise Exception # without arguments
raise Exception('abc') # with arguments
a combination of ast
module functionality and inspect.getclosurevars
(to get exception classes, was introduced in Python 3.3) can produce quite accurate results:
from inspect import getclosurevars, getsource
from collections import ChainMap
from textwrap import dedent
import ast, os
class MyException(Exception):
pass
def g():
raise Exception
class A():
def method():
raise OSError
def f(x):
int()
A.method()
os.makedirs()
g()
raise MyException
raise ValueError('argument')
def get_exceptions(func, ids=set()):
try:
vars = ChainMap(*getclosurevars(func)[:3])
source = dedent(getsource(func))
except TypeError:
return
class _visitor(ast.NodeTransformer):
def __init__(self):
self.nodes = []
self.other = []
def visit_Raise(self, n):
self.nodes.append(n.exc)
def visit_Expr(self, n):
if not isinstance(n.value, ast.Call):
return
c, ob = n.value.func, None
if isinstance(c, ast.Attribute):
parts = []
while getattr(c, 'value', None):
parts.append(c.attr)
c = c.value
if c.id in vars:
ob = vars[c.id]
for name in reversed(parts):
ob = getattr(ob, name)
elif isinstance(c, ast.Name):
if c.id in vars:
ob = vars[c.id]
if ob is not None and id(ob) not in ids:
self.other.append(ob)
ids.add(id(ob))
v = _visitor()
v.visit(ast.parse(source))
for n in v.nodes:
if isinstance(n, (ast.Call, ast.Name)):
name = n.id if isinstance(n, ast.Name) else n.func.id
if name in vars:
yield vars[name]
for o in v.other:
yield from get_exceptions(o)
for e in get_exceptions(f):
print(e)
prints
<class '__main__.MyException'>
<class 'ValueError'>
<class 'OSError'>
<class 'Exception'>
Keep in mind that this code only works for functions written in Python.
How to find the kind of errors a method may throw and catch them in Swift
NSError
automatically bridges to ErrorType
where the domain becomes the type (e.g. NSCocoaErrorDomain
becomes CocoaError
) and the error code becomes the value (NSFileReadNoSuchFileError
becomes .fileNoSuchFile
)
import Foundation
let docsPath = "/file/not/found"
let fileManager = FileManager()
do {
let docsArray = try fileManager.contentsOfDirectoryAtPath(docsPath)
} catch CocoaError.fileNoSuchFile {
print("No such file")
} catch let error {
// other errors
print(error.localizedDescription)
}
As for knowing which error can be returned by a specific call, only the documentation can help. Almost all Foundation errors are part of the CocoaError
domain and can be found in FoundationErrors.h
(though there are some rare bugs where Foundation can also sometimes return POSIX errors under NSPOSIXErrorDomain
) but these ones might not have been fully bridged so you will have to fall back on managing them at the NSError
level.
More information can be found in « Using Swift with Cocoa and Objective-C (Swift 2.2) »
How can I know which exceptions might be thrown from a method call?
I guess a solution could be only imprecise because of lack of static typing rules.
I'm not aware of some tool that checks exceptions, but you could come up with your own tool matching your needs (a good chance to play a little with static analysis).
As a first attempt, you could write a function that builds an AST, finds all Raise
nodes, and then tries to figure out common patterns of raising exceptions (e. g. calling a constructor directly)
Let x
be the following program:
x = '''\
if f(x):
raise IOError(errno.ENOENT, 'not found')
else:
e = g(x)
raise e
'''
Build the AST using the compiler
package:
tree = compiler.parse(x)
Then define a Raise
visitor class:
class RaiseVisitor(object):
def __init__(self):
self.nodes = []
def visitRaise(self, n):
self.nodes.append(n)
And walk the AST collecting Raise
nodes:
v = RaiseVisitor()
compiler.walk(tree, v)
>>> print v.nodes
[
Raise(
CallFunc(
Name('IOError'),
[Getattr(Name('errno'), 'ENOENT'), Const('not found')],
None, None),
None, None),
Raise(Name('e'), None, None),
]
You may continue by resolving symbols using compiler symbol tables, analyzing data dependencies, etc. Or you may just deduce, that CallFunc(Name('IOError'), ...)
"should definitely mean raising IOError
", which is quite OK for quick practical results :)
How to test the type of a thrown exception in Jest
In Jest you have to pass a function into expect(function).toThrow(<blank or type of error>)
.
Example:
test("Test description", () => {
const t = () => {
throw new TypeError();
};
expect(t).toThrow(TypeError);
});
Or if you also want to check for error message:
test("Test description", () => {
const t = () => {
throw new TypeError("UNKNOWN ERROR");
};
expect(t).toThrow(TypeError);
expect(t).toThrow("UNKNOWN ERROR");
});
If you need to test an existing function whether it throws with a set of arguments, you have to wrap it inside an anonymous function in expect()
.
Example:
test("Test description", () => {
expect(() => {http.get(yourUrl, yourCallbackFn)}).toThrow(TypeError);
});
Related Topics
Ios: Is Core Graphics Implemented on Top of Opengl
Change Tab Bar Tint Color on iOS 7
How to Restrict Uitextfield to Take Only Numbers in Swift
How to Fade a Uivisualeffectview And/Or Uiblureffect in and Out
How to Programmatically Add a Uisegmentedcontrol to a Container View
How to Check If a Static Library Is Built for 64-Bit
Nsuserdefaults Losing Its Keys & Values When Phone Is Rebooted But Not Unlocked
Xctest/Xctest.H Not Found on Old Projects Built in Xcode 6
iOS 7 Uitableview: How to Remove Space Between Navigation Bar and First Cell
How to Get the Rootviewcontroller from a Pushed Controller
Add Rounded Corners to All Uiimageviews
Watchkit Extension Bundle Identifiers
iOS Multiline Label in Interface Builder
Intercept Objective-C Delegate Messages Within a Subclass