[Tutor] Debug help

Cameron Simpson cs at cskk.id.au
Wed Jul 22 19:49:40 EDT 2020


On 22Jul2020 16:39, jim <jf_byrnes at comcast.net> wrote:
>On 7/19/20 6:57 PM, Cameron Simpson wrote:
>>On 19Jul2020 15:25, jim <jf_byrnes at comcast.net> wrote:
>>[...]
>>>Here is the part of the code that generates the error, the remainder of
>>>the script just manipulates the spreadsheet.
>>>
>>>    for stock in stocks:
>>>        # ~ count = 0
>>>        # ~ while count < total_num_of_stocks:
>>>        # ~ while count < 33:
>>>        while True:
>>>            # ~ count = count + 1
>>>            try:
>>>                # ~ print('Day = ' + day)
>>>                ticker = yf.Ticker(stock)
>>>                # ~ print('Ticker = ' + ticker)
[...]
>
>I am finally able to get back to this discussion. Thanks for the great 
>explanation of try/except. I was always confused when people talked 
>about only catch what you expect and can handle. I googled python 
>exceptions and found a list of them. It makes more sense now.

A random google found reference might not be ideal (not to mention that 
programmes can invent their own). Consider these links:

    The Builtin Exceptions
    https://docs.python.org/3/library/exceptions.html

    Exceptions in the Python execution model
    https://docs.python.org/3/reference/executionmodel.html#exceptions

    Errors and Exceptions from the Python Tutorial
    https://docs.python.org/3/tutorial/errors.html

>The error that promoted my question was:
>
>uno.RuntimeException: Couldn't convert Date
>2020-07-17    134858.5
>2020-07-17    134858.5
>Name: Close, dtype: float64 to a UNO type; caught exception: <class 
>'AttributeError'>: 'Series' object has no attribute 'getTypes', 
>traceback follows
>
>This comes from the module ooSheet, because it was given data in a 
>form it did not expect.   Can try/except be used to catch exemptions 
>from an imported module? In this case I would want the program to 
>stop, but I imagine there could be a case where I would want it to 
>continue.

Not easily. Sometimes not at all. This is because the place where 
something could most usefully be done is buried deep in the code. By the 
time the exception reaches you too much follow on logic has been 
skipped, and you won't have whatever data should have been returned. If 
you're getting a series of data from a generator, the generator will 
have exiting with the exception and all the following data will be 
missing, and so forth.

You can of course catch the exception and continue your own local 
control structure eg the next loop iteration.

Your choices depend on how the exception occurred and the internal 
structure of the module you're using.

Have a look at the stack trace - it will identify where the exception 
was raised. This includes the soruce code references!

Options include:

Call something lower level. Suppose you code is calling a function which 
returns spreadsheet like data, some table of values from the stock data.  
Likely that function itself calls lower level things to process the data 
and construct the array. You could do that work yourself, and call some 
per-row function, and catch the exception on a row-by-row basis, 
skipping (and reporting in detail) the bad row.

Pass in a special parser. Some libraries have a degree of extensibility 
in mind, and have a hook for a custom parser, i.e. a functioin to 
convert the raw data into whatever data type you intend to get. If 
there's such a hook, use it. Initially you can find out the default 
parser function, and write a small function which:
- calls the default with a try/except
- catches exceptions and reports the exception and the dfata which 
  raised it, and return maybe None

For example:

    # you'd need to find this, if it exists
    default_parser = module.something

    def my_parser(row):
        try:
            return default_parser(row)
        except Exception as e:
            print("default_parser fails on row %r with exception %s" % (row, e))
            return None

    data = ticker.fetch(parser=my_parser)

Obviously this requires the module to offer this facility.

Monkey patch! Modify the modul in place.

Monkey patching is where you alter a module after it is loaded. In your 
case, look at the module source code and find the low level function 
which raised the exception. This is available in the stack trace!

Let's suppose the low level thing is module.parse_datum(), to invent a 
name. You can do the custom parser with a monkey patch:

    import yf

    # you'd need to find this, if it exists
    original_parser = yf.parse_datum

    def my_parse_datum(datum):
        try:
            return original_parse_datum(datum)
        except Exception as e:
            print("yf.parse_datum fails on %r with exception %s" % (datum, e))
            # you could parse datum specially here, if you know what to 
            # handle
            return None

    # monkey patch here:
    yf.parse_datum = my_parse_datum

    ...
    yf.Ticker(...)
    ...

Now your special function is in the module where the original parser 
was, and _uses_ the original parse unless it fails.

If nothing else, this provides the data causing the trouble. And you can 
then write your own code to handle just that data when it shows up!

Cheers,
Cameron Simpson <cs at cskk.id.au>


More information about the Tutor mailing list