Error while working with excel using python
Asked Answered
G

4

14

while my script is updating one excel same time if i am going to do any other work manually with another excel error occurs i am using dispatch

     from win32com.client import Dispatch

     excel    = Dispatch('Excel.Application')
excel.Visible   = True 

file_name="file_name.xls"
workbook = excel.Workbooks.Open(file_name)
workBook  = excel.ActiveWorkbook
sheet=workbook.Sheets(sheetno)

I am geting error like this (, com_error(-2147418111, 'Call was rejected by callee.', None, None)

Is there is any way to overcome it ..can i update another excel without geting error..

Gutow answered 15/9, 2010 at 13:26 Comment(1)
Include the entire traceback so we can see what line caused the error. A few comments: Firstly, you have two variables workbook and workBook that differ only by one capital letter. Probably not a good idea. Secondly, there is no reason to grab the ActiveWorkbook as you already have a reference to it on the previous line (an opened workbook is immediately active). Thirdly, you do not specify a path for your filename, so if your python code and excel worksheet are in different directories, Open will fail. Finally, perhaps sheetno is out of range. Excel holds sheets in a 1-based array.Beaulieu
P
18

I encountered this same issue recently. While it sounds like there can be multiple root causes, my situation was occurring because Python was making subsequent calls too quickly for Excel to keep up, particularly with external query refreshes. I resolved this intermittent "Call was rejected by callee" error by inserting time.sleep() between most of my calls and increasing the sleep argument for any calls that are particularly lengthy (usually between 7-15 seconds). This allows Excel the time to complete each command before Python issued additional commands.

Projective answered 20/7, 2017 at 23:54 Comment(0)
P
9

This error occurs because the COM object you're calling will reject an external call if it's already handling another operation. There is no asynchronous handling of calls and the behavior can seem random.

Depending on the operation you'll see either pythoncom.com_error or pywintypes.com_error. A simple (if inelegant) way to work around this is to wrap your calls into the COM object with try-except and, if you get one of these access errors, retry your call.

For some background see the "Error Handling" section of the chapter 12 excerpt from Python Programming on Win32 by Mark Hammond & Andy Robinson (O'Reilly 2000).

There's also some useful info specifically about Excel in Siew Kam Onn's blog post "Python programming with Excel, how to overcome COM_error from the makepy generated python file".

Permanence answered 22/3, 2011 at 23:19 Comment(0)
C
3

I have been struggling with the same problem, but now I have made a solution that works for me so far.

I created a class, ComWrapper, that I wrap the Excel COM object in. It automatically wraps every nested object and call in ComWrapper, and unwraps them when they are used as arguments to function calls or assignments to wrapped objects. The wrapper works by catching the "Call was rejected by callee"-exceptions and retrying the call until the timeout defined at the top is reached. If the timeout is reached, the exception is finally thrown outside the wrapper object.

Function calls to wrapped objects are automatically wrapped by a function _com_call_wrapper, which is where the magic happens.

To make it work, just wrap the com object from Dispatch using ComWrapper and then use it as usual, like at the bottom of the code. Comment if there are problems.

import win32com.client
from pywintypes import com_error
import time
import logging

_DELAY = 0.05  # seconds
_TIMEOUT = 60.0  # seconds


def _com_call_wrapper(f, *args, **kwargs):
    """
    COMWrapper support function. 
    Repeats calls when 'Call was rejected by callee.' exception occurs.
    """
    # Unwrap inputs
    args = [arg._wrapped_object if isinstance(arg, ComWrapper) else arg for arg in args]
    kwargs = dict([(key, value._wrapped_object)
                   if isinstance(value, ComWrapper)
                   else (key, value)
                   for key, value in dict(kwargs).items()])

    start_time = None
    while True:
        try:
            result = f(*args, **kwargs)
        except com_error as e:
            if e.strerror == 'Call was rejected by callee.':
                if start_time is None:
                    start_time = time.time()
                    logging.warning('Call was rejected by callee.')

                elif time.time() - start_time >= _TIMEOUT:
                    raise

                time.sleep(_DELAY)
                continue

            raise

        break

    if isinstance(result, win32com.client.CDispatch) or callable(result):
        return ComWrapper(result)
    return result


class ComWrapper(object):
    """
    Class to wrap COM objects to repeat calls when 'Call was rejected by callee.' exception occurs.
    """

    def __init__(self, wrapped_object):
        assert isinstance(wrapped_object, win32com.client.CDispatch) or callable(wrapped_object)
        self.__dict__['_wrapped_object'] = wrapped_object

    def __getattr__(self, item):
        return _com_call_wrapper(self._wrapped_object.__getattr__, item)

    def __getitem__(self, item):
        return _com_call_wrapper(self._wrapped_object.__getitem__, item)

    def __setattr__(self, key, value):
        _com_call_wrapper(self._wrapped_object.__setattr__, key, value)

    def __setitem__(self, key, value):
        _com_call_wrapper(self._wrapped_object.__setitem__, key, value)

    def __call__(self, *args, **kwargs):
        return _com_call_wrapper(self._wrapped_object.__call__, *args, **kwargs)

    def __repr__(self):
        return 'ComWrapper<{}>'.format(repr(self._wrapped_object))


_xl = win32com.client.dynamic.Dispatch('Excel.Application')
xl = ComWrapper(_xl)

# Do stuff with xl instead of _xl, and calls will be attempted until the timeout is
# reached if "Call was rejected by callee."-exceptions are thrown.

I gave the same answer to a newer question here: https://mcmap.net/q/828130/-call-was-rejected-by-callee-in-win32com-if-a-dialog-box-is-open-or-excel-is-otherwise-waiting-for-the-user

Cognate answered 1/5, 2019 at 5:10 Comment(1)
Thanks. I had to add time.sleep(1) at the beginning of _com_call_wrapper() to make it work. In the future, I'll avoid Excel whenever I can.Trophic
R
2

I run intensive excel sheets which consistently show this (blocking) error whilst the calculation cycle runs.

The solution is to use a for loop.

I provide the section of my code solution which works:

# it failed, keep trying
attempt_number = 0
reading_complete = False
while reading_complete==False:
    try:
        workbook = xw.Book(file_to_read)
        reading_complete = True
        print('file read...')
    except:
        reading_complete = False
        attempt_number += 1
        print('attempt:', attempt_number)
        if attempt_number > 5:
            print('no good: exiting the process')
            exit()

Where:

  • The file_to_read is the full path and name of the excel workbook.
  • The attempt_number variable is set to limit the number of attempts.
Reticulum answered 26/11, 2021 at 15:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.