Search code examples
pythonglobal-variableslscustom-error-handling

Is it dubious practice to reference a global variable in a custom error_handler for python's str() function?


Is it bad/dubious/allowable practice to reference (or change) a global variable in a custom error_handler for the str() function, that is set with codecs.register_error()? (see https://docs.python.org/3.6/library/codecs.html#codecs.register_error.)

I am trying to implement a customized 'backslashreplace' function that, in addition to backslashescaping, also wraps the result in either single quotes (') or double quotes ("), very much like the gnu ls program does with filenames when --quoting-style=shell-escape.

The problem is, the choice between single or double quotes cannot be transmitted to the error handler. The only way to let it know which to use, is for it to reference a global variable that flags whether or not single/double quotes should be used.

(I am using Python version 3.6.9).

Here's an example program:

#!/usr/bin/env python3

import codecs

# in my program, quote varies between these two at runtime
#quote = "'"
quote = '"'


def my_replace( e ):
    global quote        # <-- global variable

    if not isinstance( e, UnicodeDecodeError ):
        raise TypeError( "don't know how to handle %r" % e )

    x = []
    for c in e.object[e.start:e.end]:
        try:
            if c == 0x93 or c == 0x94:
                x.append( quote + ( "$'\\%o'" % c) + quote )
        except KeyError:
            return( None )

    return( "".join(x), e.end )


codecs.register_error( "my_replace", my_replace )

s = b'61. \x93Gleich wie ein Hirsch begehret\x94, P.169_ IV. Variatio 3.flac'
s = str( s, 'utf-8', errors='my_replace' )
print( quote + s + quote )

Solution

  • Using a global var just to store and later read a setting from one or more places, looks OK to me. Specially given it is very simple to do.

    For a different idea, have you thought of using a closure for your handler, something like below:

    def outer(quote):
        settings = dict(quote=quote)
        def inner():
            print(settings['quote'])
        return inner
    
    error_handler = outer("'")
    
    # Then you register your error_handler...
    # Later when called it remembers the settings
    error_handler() # prints the simple quote
    

    Taking your comment in consideration, use a class instead of a closure:

    class QuotedErrorHandler:
        quote = "'"
    
        def handler(self, error):
            # do your thing
            print("Quote to use: {}".format(QuotedErrorHandler.quote))
            return error.upper()
    
    QuotedErrorHandler.quote = '"'
    my_handler = QuotedErrorHandler()
    error_handler = my_handler.handler
    
    print(error_handler("Some error"))
    print(my_handler.quote)