Package: retval
retvalA MIT-licensed Python module for better function return values without exceptions.DescriptionThis module provides RetVal, a class which powers up how you handle errors and return values. With RetVal you can:Use more specific error codesPull from a small library of common errorsProvide context information with errorsCreate your own errors quickly -- no more writing custom Exception classesReturn a variable number of data values from functionsStatusThe module is production stable and in active use.UsageMost usage of RetVal revolves around using the constructor to quickly package error information.importjsonfromretvalimportRetVal,ErrBadType,ErrEmptyData,ErrExists# Creating new error constants is super simpleErrDecryptionFailure='decryption failure'defsave(self,path:str)->RetVal:'''Saves the key to a file'''ifnotpath:# The second argument, which may be omitted, is for context information for the developer.# It is not intended to be user-facing.returnRetVal(ErrEmptyData,'path may not be empty')ifos.path.exists(path):returnRetVal(ErrExists,f"{path}exists")try:fhandle=open(path,'w')json.dump({'SecretKey':self.get_key()},fhandle,ensure_ascii=False,indent=1)fhandle.close()exceptExceptionase:# This little gem allows you to elegantly handle any exception the same way as any other# errorreturnRetVal().wrap_exception(e)returnRetVal()defdecrypt(self,encrypted_data:str)->RetVal:'''Decrypts Base85-encoded encrypted data and returns it as bytes in the 'data' field.'''# It is highly recommended to mention returned fields in the docstring.ifnotencrypted_data:returnRetVal(ErrEmptyData)ifnotisinstance(encdata,str):returnRetVal(ErrBadType)secretbox=nacl.secret.SecretBox(self.key.raw_data())try:decrypted_data=secretbox.decrypt(encrypted_data,encoder=Base85Encoder)except:returnRetVal(ErrDecryptionFailure)returnRetVal().set_value('data',decrypted_data)Unless your code uses a lot of different error messages, for maximum code clarity it is recommended to import the RetVal class and any error codes used directly into your code as demonstrated above. An empty constructor indicates success. Call-chaining is the most efficient way to return a single value using RetVal, as demonstrated indecrypt(), and multiple values can be call-chained usingset_values().Because RetVal is intended to be treated as a dictionary with some extra features, using a RetVal returned from a function is very simple.pubkey=PublicKey(organization_key)status=pubkey.encrypt(tagstr.encode())ifstatus.error():returnstatusreturnstatus['data']Theencrypt()function called in the above example is similar todecrypt(), takingbytesand returning a RetVal containing the encrypted data in thedatafield. Most of the time, dealing with a function which returns a RetVal is just a matter of checking for an error state and returning the entire RetVal object if there is one and accessing the data in the object if there isn't.HistoryThis module exists because I was inspired to think about Python error-handling and return values after spending more than a little time learning Go. Go errors are little more than strings, which isn't great, but they are often paired with other return values. If no error is returned, then the other return value is safe to consider as valid. Go also doesn't have very many built-in error codes and are not very well documented. RetVal takes from the good and builds upon it. It integrates pretty easily into existing code and the extra contextual information makes debugging significantly easier. Is it Pythonic? Most likely not. I don't care... it's made error-handling easier for me and I'm sure it will for you, too.BuildingThis is a very simple but useful module. Most people will just want to install it from pip. Runningpython setup.py installshould be the thing needed if you want to install from the tarball.