Package: errator
Provide human-readable error narration of exception tracebacks witherrator.What’s new in 0.4IntroHow it worksRequirementsInstallingQuick StartBuilding from sourceWhat’s new in 0.4This has largely been a maintenance release which focused on two aspects:Get the ‘manylinux’ builds in order.Add type annotations where it made sense.However, it also fixed a bug where if a fragment formatting function raised an exception
it would not provide any data for that fragment (and possibly any others).There is also a new capability: you can now add atagskeyword argument to bothnarrate()andnarrate_cm()to allow selective retrieval of narration fragments
with theget_narration()function using thewith_tagskeyword argument to that
function. In either case, the tags are a list of strings. Ifnarrate()ornarrate_cm()are used without tags, then their fragments are always included in the return fromget_narration(). Ifget_narration()doesn’t specify any tags, then every current
fragement is returned regards of the absence or presence of tags. Otherwise, when usingget_narration()with tags, a fragment must have one or more tags in common with those
specified inget_narration()in order to be returned from that function.What’s new in 0.3This is largely a performance release forerrator, as it implements the more costly part of the narration process in aCythonextension. Additionally, there has been a change in the way verbose narrations are performed, as the original approach entailed a lot of overhead even if verbose narrations weren’t required.erratornow consists of a Python file and a  Python extension module (built from a Cython source), which improves runtime performanceThe ‘verbose’ keyword argument has been removed from theget_narration()function, and has been added to theset_narration_options()andset_default_options()functions. Now getting verbose narrations is a matter of setting the option either as a default or on a per-thread basis, hence this option must be setbeforecollecting narrations.Previously, detailed exception information (file name, line number) was always collected in case a user wanted to fetch verbose narration information withget_narration(). However, this proved to be a big performance hit, and now this information isonlycollected if the ‘verbose’ option has been set withset_narration_options()orset_default_options()functionbeforethe exception occurs.What’s new in 0.2.1This patch release addresses certain performance issues:Patched a bug that caused too many object creations to occurRemoved some redundant resetting of state in narration fragment objectsAdded caching of filenames for function objects so that lookups aren’t always requiredSped up the resetting processWhat’s new in 0.2Theget_narration()now has a new keyword argument, “verbose” (default False), that when True returns expanded narration information that includes the line number, function, and file name of the point in the stack trace that the narration applies to.To provide a more tidy display of stack information,erratornow has analogs of the functions from the standardtracebackmodule that filter outerrator-based calls from the call stack, leaving only application calls in the display of stack traces.Narration output formatting has been modified slightly.Introerratorcame as an idea on the back of trying to figure out what the semantics of an exception traceback are in non-trivial pieces of code.When an exception occurs deep inside a call stack within some generic utility function that is used in numerous contexts, reading the traceback is often not helpful in determining the source of the problem. Data values aren’t obvious, and the initial starting conditions of the error can’t easily been seen.Logging is a step in the right direction, but in general outputs too much information; often, there is lots of info regarding error-free processing in the log, making it hard to find the right log output that is associated with the error.erratoris something of a marriage between logging and tracebacks: plain text messages that are associated directly with the call trail that led to an exception.erratorworks by providing tools that let you state intent of code in text, but only captures that text when an exception bubbles up the stack. You can then acquire this “error narration” and display it to your user in the most appropriate fashion.How it workserratoruses decorators and context managers to maintain a stack of “narration fragments” behind the scenes. When code executes without exceptions, these fragments are created and thrown away as narrated code executes and returns. However, when an exception is raised, the narration fragments are retained and their content can be retrieved. Fragments can be automatically discarded (pruned) when an exception doesn’t propagate any further up the stack, or can be discarded under user control, allowing more control over the content of “narration” provided for the exception.erratoris thread-safe, allowing you to capture separate error narrations for independent threads of control through the same code.Requirementserratordoesn’t have any runtime external dependencies, and is compatible with Python 2.7 and 3.x. If you wish to build from source, you’ll need to install Cython to build the extension module, and have a C compiler and the required Python header files.InstallingAs of 0.3,erratoris comprised of a Python module and an extension. You can install it withpip install errator, or you can clone the Git project and build from source (more on this below).Quick StartThe next section discusseserratorwith functions, but you can also use the decorators described with methods too.Basic UseStart by importingerratorinto the module that you want to narrate:fromerratorimport*Now, suppose you have a utility function that performs some specialized string formatting, but it is possible to pass in arguments that cause a exception to be raised. Your function is called all over the place for a variety of different reasons, often very deep down the call stack where it isn’t obvious what the original functional intent was, or where the source of bad arguments may have been.To start building the narration to your function’s execution, you can use thenarrate()decorator to associate a bit of text with your utility function in order to provide easily understandable explanations about what’s going on:@narrate("I'm trying to format a string")defspecial_formatter(fmt_string,**kwargs):# magic format code that sometimes raises an exceptionThenarrate()decorator knows to look for exceptions and doesn’t impede their propagation, but captures that bit of text in an internal stack when an exception occurs. So if you write:try:s=special_formatter(fmt,**args)exceptionException:the_tale=get_narration()…andspecial_formatter()raises an exception, the exception will still bubble up the stack, butget_narration()will return a list of strings for all thenarrate()-decorated functions down to the exception. If no exception is raised, there are no strings to fetch (unless you want there to be strings, but we’ll get to that).Getting more informationMaybe you’d like some insight as to the value of the arguments passed when an exception is raised, so you can better tell what’s causing it. Instead of a string, you can supply thenarrate()decorator with a callable that returns a string and that has the same signature as the function being decorated. This callable willonly be invoked if the decorated function raises an exception, and gets invoked with the same arguments as the function:@narrate(lambdafs,**kw:"I'm trying to format a string with '%s' and args '%s'"%(fs,str(kw)))defspecial_formatter(fmt_string,**kwargs):# magic format code that sometimes raises an exceptionThe lambda passed to narrate() will only be called whenspecial_formatter()raises an exception, otherwise it will go un-executed.Finer details with contextsNow, perhapsspecial_formatter()is a rather long function, and you’d like to be able to narrate it’s operation in more detail to get better narrations when things go wrong. You can use thenarrate_cm()context manager to create a narration fragment for a block of code. If everything goes well in the block, then the fragment is discarded, but the fragment will be retained if an exception occurs:defspecial_formatter(fmt_string,**kwargs):forformat_tokeninparse_format(fmt_string):ifformat_token.type==float:withnarrate_cm("I started processing a float format"):# do magic stuff for floats...elifformat_token.type==int:withnarrate_cm("I started processing an int format"):# do magic stuff for ints...Narration fragments added withnarrate_cm()are treated just like those created by the function decorator– they are added to the stack, and silently removed if the context manager’s code block exits normally. But exceptions raised in the context block are retained as the exception propagates back through the stack.Likenarrate(),narrate\_cm()allows you to supply a callable instead of
a string:withnarrate_cm(lambdax:"I started processing an int with format%s"%x,format_token.format):# format code…and again, this callable will only be invoked if an exception is raised in the context. Unlikenarrate(), however, you are free to define a callable with any signature, as long as you supply the arguments needed to invoke the callable if need be.Context managers may nest, and in fact any combination of function decorator and context manager will work as expected.A larger exampleLet’s look at an example with more complex calling relationships. Suppose we have functionsA,B,C,D,E, andF. They have the following calling relationships:AcallsBthenCBcallsDCcallsEorFDcallsFWe’ll make it so that if we’re unlucky enough to callE, we’ll get an exception raised. This will happen only for input values ofAgreater than 10.So let’s define these functions and narrate them– paste these into an interactive Python session after you’ve importederrator:@narrate(lambdav:"I'm trying to A with%sas input"%v)defA(val):B(val/2)C(val*2)@narrate(lambdav:"I'm trying to B with%sas input"%v)defB(val):D(val*10)@narrate(lambdav:"I'm trying to C with%sas input"%v)defC(val):ifval>20:E(val)else:F(val)@narrate(lambdav:"I'm trying to D with%sas input"%v)defD(val):F(val*3)@narrate(lambdav:"I'm trying to E with%sas input"%v)defE(val):raiseValueError("how dare you call me with such a value?")@narrate(lambdav:"I'm trying to F with%sas input"%v)defF(val):print("very well")Now runAwith a value less than 11, and look for narration text:>>>A(3)verywellverywell>>>get_narration()[]>>>Since there was no exception, there are no narrations. Now runAwith a value greater than 10, which will cause an exception in E:>>>A(11)verywellTraceback(mostrecentcalllast):File"<stdin>",line1,in<module>File"errator.py",line322,incallit_v=m(*args,**kwargs)File"<stdin>",line4,inAFile"errator.py",line322,incallit_v=m(*args,**kwargs)File"<stdin>",line4,inCFile"errator.py",line322,incallit_v=m(*args,**kwargs)File"<stdin>",line3,inEValueError:howdareyoucallmewithsuchavalue?>>>So far, it’s as we’d expect, except perhaps for the inclusion oferratorcalls in the stack (erratorincludes tools that allow you to get stack traces that have been cleaned oferratorcalls). But now let’s look at the narration:>>> for l in get_narration():
...     print(l)
...
I'm trying to A with 11 as input
I'm trying to C with 22 as input
I'm trying to E with 22 as input, but exception type: ValueError, value: how dare you call me with such a value? was raised
>>>We have a narration for our recent exception. Now try the following:>>>A(8)verywellverywell>>>get_narration()["I'm trying to A with 11 as input","I'm trying to C with 22 as input",# etc...Wait, this didn’t have an exception; why is there still error narration? This is becausean error narration only gets cleared out if a decorated function does NOT have an exception bubble up; the assumption is that the exception was caught and the narration was retrieved, so a decorated function that returns normally would remove the previous narration fragments. In our example, there is no function that is decorated withnarrate()that catches the exception and returns normally, so the narration never clears out.There are a few ways to clear unwanted narrations: first is to manually clear the narration, and the other is to make sure you have a decorated function that catches the exception and returns normally, which will clear the narration automaticallyTo manually clear narrations we callreset_narration():>>>reset_narration()>>>get_narration()>>>[]For the second, if we define a decorated function that calls A but which handles the exception and returns normally, the narration fragments will be cleaned up automatically:@narrate("Handler for A")deffirst(val):try:A(val)except:print("Got%dnarration lines"%len(get_narration()))This outermost function still can retrieve the narration, but as it returns normally, the narration is cleared out when it returns:>>>first(11)verywellGot4narrationlines>>>get_narration()[]>>>erratorprovides various narration options and finer degrees of control for retriving the narration; these are covered in the detailed docs. See theusing_erratorfile in the docs directory.Building from sourceerratoris built using Cython, however the source package contains the generated C file, so you only need:A C compilerPython header files for your version of PythonFor development, in the project root, run the following command:python setup.py build_ext --inplace…This will create the shared library that is used byerrator. You can then do the normalpython setup.py installdance to put the built distribution where you want it to go, or you can simply use it right from where you built it.If you want to build a wheel, the command is:python setup.py bdist_wheelIf you wish to build from the Cython pyx file, you’ll need to grab the source from the Github repo and run the same commands as above; they will run Cython when appropriate.