advanced programming techniques in debug mode the global variable __debug__ is true if this is the case we set up logging using the logging moduleand then create the @logged decorator the logging module is very powerful and flexible--it can log to filesrotated filesemailsnetwork connectionshttp serversand more here we've used only the most basic facilities by creating logging objectsetting its logging level (several levels are supported)and choosing to use file for the output dictionary comprehensions the wrapper' code begins by setting up the log string with the function' name and arguments we then try calling the function and storing its result if any exception occurs we store it in all cases the finally block is executedand there we add the return value (or exceptionto the log string and write to the log if no exception occurredthe result is returnedotherwisewe reraise the exception to correctly mimic the original function' behavior if python is running in optimized mode__debug__ is falsein this case we define the logged(function to simply return the function it is givenso apart from the tiny overhead of this indirection when the function is first createdthere is no runtime overhead at all note that the standard library' trace and cprofile modules can run and analyse programs and modules to produce various tracing and profiling reports both use introspectionso unlike the @logged decorator we have used hereneither trace nor cprofile requires any source code changes function annotations |functions and methods can be defined with annotations--expressions that can be used in function' signature here' the general syntaxdef functionname(par exp par exp parn expn-rexpsuite every colon expression part (expxis an optional annotationand so is the arrow return expression part (-rexpthe last (or onlypositional parameter (if presentcan be of the form *argswith or without an annotationsimilarlythe last (or onlykeyword parameter (if presentcan be of the form **kwargsagain with or without an annotation if annotations are present they are added to the function' __annotations__ dictionaryif they are not present this dictionary is empty the dictionary' keys are the parameter namesand the values are the corresponding expressions the syntax allows us to annotate allsomeor none of the parameters and to annotate the return value or not annotations have no special significance to python the only thing that python does in the face of annotations is to put them in the __annotations__ dictionaryany other action is up to us here is an example of an annotated function that is in the util module