smartsheet / smartsheet-python-sdk

Library that uses Python to connect to Smartsheet services (using API 2.0).
Apache License 2.0
44 stars 19 forks source link

KeyError: 'content-type' when using delete_rows #43

Closed qprotex closed 1 week ago

qprotex commented 5 months ago

Describe the bug Since March 6th, delete_rows has consistently generated the error KeyError: 'content-type'. Before, the script worked fine for months.

To Reproduce Steps to reproduce the behavior:

  1. Create a smartsheet instance
  2. Get a specific sheet
  3. Call smart.Sheets.delete_rows inside a loop
smart = smartsheet.Smartsheet(access_token=xxx, max_retry_time=60)
sheet_id = xxx

chunk_interval=300
while True:
    sheet = smart.Sheets.get_sheet(sheet_id)
    rows_to_delete = [row.id for row in sheet.rows]
    if not rows_to_delete:
        break
    for x in range(0, len(rows_to_delete), chunk_interval):
        smart.Sheets.delete_rows(sheet.id, rows_to_delete[x:x + chunk_interval])

Expected behavior Rows should be deleted

Screenshots image

Environment (please complete the following information):

1npo commented 3 months ago

I also get this error intermittently when calling Home.list_all_contents() and Sheets.get_sheet(). It seems to happen for no rhyme or reason, because the requests usually succeed, and I only get this error occasionally. I'm having this issue now, see below for an example.

To reproduce:

  1. Initialize a Smartsheet object
  2. Call Home.list_all_contents()

Expected behavior: I should receive a list of all the Smartsheet objects associated with my account

Environment:

Python 3.9.2 (tags/v3.9.2:1a79785, Feb 19 2021, 13:44:55) [MSC v.1928 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.16.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import smartsheet
   ...: smart = smartsheet.Smartsheet()
   ...: contents = smart.Home.list_all_contents(include='source').to_dict()
   ...: print(f'{len(contents)=}')
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[1], line 3
      1 import smartsheet
      2 smart = smartsheet.Smartsheet()
----> 3 contents = smart.Home.list_all_contents(include='source').to_dict()
      4 print(f'{len(contents)=}')

File C:\oaktier-env\lib\site-packages\smartsheet\home.py:144, in Home.list_all_contents(self, include, exclude)
    142 expected = "Home"
    143 prepped_request = self._base.prepare_request(_op)
--> 144 response = self._base.request(prepped_request, expected, _op)
    146 return response

File C:\oaktier-env\lib\site-packages\smartsheet\smartsheet.py:255, in Smartsheet.request(self, prepped_request, expected, operation)
    239 def request(self, prepped_request, expected, operation):
    240     """
    241     Make a request from the Smartsheet API.
    242
   (...)
    253         The API operation result object.
    254     """
--> 255     res = self.request_with_retry(prepped_request, operation)
    256     native = res.native(expected)
    258     if not self.raise_exceptions:

File C:\oaktier-env\lib\site-packages\smartsheet\smartsheet.py:361, in Smartsheet.request_with_retry(self, prepped_request, operation)
    359 pre_redact_request = prepped_request.copy()
    360 while True:
--> 361     result = self._request(prepped_request, operation)
    362     if isinstance(result, OperationErrorResult):
    363         native = result.native("Error")

File C:\oaktier-env\lib\site-packages\smartsheet\smartsheet.py:333, in Smartsheet._request(self, prepped_request, operation)
    331 try:
    332     res = self._session.send(prepped_request, stream=stream)
--> 333     self._log_request(operation, res)
    334 except requests.exceptions.SSLError as rex:
    335     raise HttpError(rex, "SSL handshake error, old CA bundle or old OpenSSL?") from rex

File C:\oaktier-env\lib\site-packages\smartsheet\smartsheet.py:290, in Smartsheet._log_request(self, operation, response)
    288     self._log.debug('{"requestBody": %s}', body_dumps)
    289 # response
--> 290 content_dumps = f'"<< {response.headers["Content-Type"]} content type suppressed >>"'
    291 if "application/json" in response.headers["Content-Type"]:
    292     content = response.content.decode("utf8")

File C:\oaktier-env\lib\site-packages\requests\structures.py:52, in CaseInsensitiveDict.__getitem__(self, key)
     51 def __getitem__(self, key):
---> 52     return self._store[key.lower()][1]

KeyError: 'content-type'

In [2]: from datetime import datetime

In [3]: datetime.now().strftime('%Y/%m/%d %H:%I:%S')
Out[3]: '2024/05/28 12:12:41'