1919from six import binary_type , string_types , text_type , BytesIO
2020from string import *
2121from types import FunctionType , ModuleType
22- try : # Python3
23- from importlib import reload
24- except ImportError :
25- pass
26- try : # Python3
27- from inspect import getfullargspec
28- except ImportError :
29- from inspect import getargspec as getfullargspec
3022try : # Python 2
23+ import __builtin__ as builtins
24+ from inspect import getargspec as getfullargspec
3125 from string import maketrans
3226except ImportError : # Python 3
27+ import builtins
28+ from importlib import reload
29+ from inspect import getfullargspec
3330 maketrans = str .maketrans
3431
3532
3633__all__ = ["add" , "add_macro" , "add_map" , "b" , "clear" , "codecs" , "decode" , "encode" , "ensure_str" , "examples" , "guess" ,
3734 "isb" , "generate_strings_from_regex" , "get_alphabet_from_mask" , "handle_error" , "is_native" ,
3835 "list_categories" , "list_encodings" , "list_macros" , "lookup" , "maketrans" , "os" , "rank" , "re" , "register" ,
3936 "remove" , "reset" , "s2i" , "search" , "stopfunc" , "BytesIO" , "_input" , "_stripl" , "CodecMacro" ,
40- "ParameterError" , " DARWIN" , "LANG" , "LINUX" , "MASKS" , "PY3" , "UNIX" , "WINDOWS" ]
37+ "DARWIN" , "LANG" , "LINUX" , "MASKS" , "PY3" , "UNIX" , "WINDOWS" ]
4138CODECS_REGISTRY = None
4239CODECS_OVERWRITTEN = []
4340CODECS_CATEGORIES = ["native" , "custom" ]
7572fix = lambda x , ref : b (x ) if isb (ref ) else ensure_str (x ) if iss (ref ) else x
7673
7774s2i = lambda s : int (codecs .encode (s , "base16" ), 16 )
75+ exc_name = lambda e : "" .join (t .capitalize () for t in re .split (r"[-_+]" , e ))
7876
7977
8078class CodecMacro (tuple ):
@@ -145,9 +143,6 @@ def __repr__(self):
145143 return "<codext.CodecMacro object for encoding %s at %#x>" % (self .name , id (self ))
146144
147145
148- class ParameterError (ValueError ):
149- __module__ = Exception .__module__
150-
151146# inspired from: https://stackoverflow.com/questions/10875442/possible-to-change-a-functions-repr-in-python
152147class Repr (object ):
153148 def __init__ (self , name , func ):
@@ -185,6 +180,14 @@ def _input(infile):
185180 return c
186181
187182
183+ def _set_exc (name , etype = "ValueError" ):
184+ if not hasattr (builtins , name ):
185+ exec ("class %s(%s): __module__ = 'builtins'" % (name , etype ))
186+ setattr (builtins , name , locals ()[name ])
187+ _set_exc ("InputSizeLimitError" )
188+ _set_exc ("ParameterError" )
189+
190+
188191def _stripl (s , st_lines , st_crlf ):
189192 if st_crlf :
190193 s = s .replace (b"\r \n " , b"" ) if isb (s ) else s .replace ("\r \n " , "" )
@@ -213,12 +216,18 @@ def add(ename, encode=None, decode=None, pattern=None, text=True, add_to_codecs=
213216 to remove the codec later
214217 """
215218 remove (ename )
216- if encode and not isinstance (encode , FunctionType ):
217- raise ValueError ("Bad 'encode' function" )
218- if decode and not isinstance (decode , FunctionType ):
219- raise ValueError ("Bad 'decode' function" )
219+ if encode :
220+ if not isinstance (encode , FunctionType ):
221+ raise ValueError ("Bad 'encode' function" )
222+ _set_exc ("%sEncodeError" % exc_name (ename )) # create the custom encode exception as a builtin
223+ if decode :
224+ if not isinstance (decode , FunctionType ):
225+ raise ValueError ("Bad 'decode' function" )
226+ _set_exc ("%sDecodeError" % exc_name (ename )) # create the custom decode exception as a builtin
220227 if not encode and not decode :
221228 raise ValueError ("At least one en/decoding function must be defined" )
229+ for exc in kwargs .get ('extra_exceptions' , []):
230+ _set_exc (exc ) # create additional custom exceptions as builtins
222231 glob = currentframe ().f_back .f_globals
223232 # search function for the new encoding
224233 @_with_repr (ename )
@@ -516,7 +525,8 @@ def code(text, errors="strict"):
516525 text = "" .join (str (ord (c )).zfill (3 ) for c in text )
517526 r = ""
518527 lsep = "" if decode else sep if len (sep ) <= 1 else sep [0 ]
519- error_func = handle_error (ename , errors , lsep , repl_char , rminlen , decode )
528+ kind = ["character" , "token" ][tmaxlen > 1 ]
529+ error_func = handle_error (ename , errors , lsep , repl_char , rminlen , decode , kind )
520530
521531 # get the value from the mapping dictionary, trying the token with its inverted case if relevant
522532 def __get_value (token , position , case_changed = False ):
@@ -722,6 +732,11 @@ def remove(name):
722732 json .dump (PERS_MACROS , f , indent = 2 )
723733 except KeyError :
724734 pass
735+ for s in ["En" , "De" ]:
736+ try :
737+ delattr (builtins , "%s%scodeError" % (name .capitalize (), s ))
738+ except AttributeError :
739+ pass
725740codecs .remove = remove
726741
727742
@@ -815,7 +830,7 @@ def get_alphabet_from_mask(mask):
815830
816831
817832# generic error handling function
818- def handle_error (ename , errors , sep = "" , repl_char = "?" , repl_minlen = 1 , decode = False , item = "position" ):
833+ def handle_error (ename , errors , sep = "" , repl_char = "?" , repl_minlen = 1 , decode = False , kind = "character" , item = "position" ):
819834 """ This shortcut function allows to handle error modes given some tuning parameters.
820835
821836 :param ename: encoding name
@@ -826,23 +841,21 @@ def handle_error(ename, errors, sep="", repl_char="?", repl_minlen=1, decode=Fal
826841 :param decode: whether we are encoding or decoding
827842 :param item: position item description (for describing the error ; e.g. "group" or "token")
828843 """
829- name = "" .join (t .capitalize () for t in re .split (r"[-_+]" , ename ))
830- # dynamically make dedicated exception classes bound to the related codec module
831- exc = "%s%scodeError" % (name , ["En" , "De" ][decode ])
832- glob = {'__name__' : "__main__" }
833- exec ("class %s(ValueError): pass" % exc , glob )
834-
835- def _handle_error (token , position , output = "" ):
844+ exc = "%s%scodeError" % (exc_name (ename ), ["En" , "De" ][decode ])
845+
846+ def _handle_error (token , position , output = "" , eename = None ):
836847 """ This handles an encoding/decoding error according to the selected handling mode.
837848
838849 :param token: input token to be encoded/decoded
839850 :param position: token position index
840851 :param output: output, as decoded up to the position of the error
841852 """
842853 if errors == "strict" :
843- msg = "'{}' codec can't {}code character '{}' in {} {}"
844- err = glob [exc ](msg .format (ename , ["en" , "de" ][decode ], token , item , position ))
854+ msg = "'%s' codec can't %scode %s '%s' in %s %d"
855+ token = token [:7 ] + "..." if len (token ) > 10 else token
856+ err = getattr (builtins , exc )(msg % (eename or ename , ["en" , "de" ][decode ], kind , token , item , position ))
845857 err .output = output
858+ err .__cause__ = err
846859 raise err
847860 elif errors == "leave" :
848861 return token + sep
0 commit comments