1

In order to check the existence of a key in a TodoistAPI class I use the "in" keyword. However, it causes a KeyError.

I isolated the code fragments to check what exactly causes the KeyError and which inputs are provided.

api = TodoistAPI(token)
api.sync()
print("id" in api.state["items"][0])

api.state["items"][0] contains:

Item({'assigned_by_uid': None,
 'checked': 0,
 'child_order': 0,
 'collapsed': 0,
 'content': 'example task',
 'date_added': '0',
 'date_completed': None,
 'day_order': 0,
 'due': {'date': '0',
         'is_recurring': True,
         'lang': 'de',
         'string': 'every day',
         'timezone': None},
 'has_more_notes': False,
 'id': 0,
 'in_history': 0,
 'is_deleted': 0,
 'labels': [0, 0],
 'parent_id': None,
 'priority': 1,
 'project_id': 0,
 'responsible_uid': None,
 'section_id': None,
 'sync_id': None,
 'user_id': 0})

I expect the output of print(...) to be True or False, but the actual output is as follows

Traceback (most recent call last):
File "/Users/path/to/file.py", line 11, in 
print("id" in applicationInterface.state["items"][0])
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/todoist/models.py", line 16, in __getitem__
return self.data[key]
KeyError: 0

Process finished with exit code 1

I also created an issue in the Todoist Github-Repository: https://github.com/Doist/todoist-python/issues/64

5t4cktr4c3
  • 151
  • 1
  • 10
  • 1
    Could you `print` the list `api.state["items"]`? I wonder what's inside. – Pavel Vergeev Oct 27 '19 at 15:09
  • @5t4cktr4c3 - Welcome to StackOverflow! Since you got the issue cleared, kindly close the Github Jira Issue. – sam Oct 27 '19 at 15:16
  • @5t4cktr4c3, could you split the statement into two statements, like this: `item = api.state["items"][0]; print("id" in item)`? It really seems like the KeyError is reporting a key of `0`. – NicholasM Oct 27 '19 at 15:31
  • @NicholasM `Traceback (most recent call last): File "/Users/path/to/file.py", line 12, in print("id" in item) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/todoist/models.py", line 16, in __getitem__ return self.data[key] KeyError: 0` – 5t4cktr4c3 Oct 27 '19 at 15:36

1 Answers1

1

Ultimately, this is a bug in the Todoist library.

First, lets imagine an additional line like this:

item = api.state["items"][0]
print("id" in item)

This means that when you write "id" in item, Python checks for a __contains__ method in the class. The Item class does not define __contains__, but does define __getitem__ (through its base Model).

According to the Python language reference, __getitem__ will be called to evaluate the in, using a fallback to a sequence protocol.

I think the logic used to evaluate "id" in item is equivalent to this:

## Implicit logic used to evaluate `"id" in item`
for i in itertools.count():
    try:
        result = item[i]
    except IndexError:
        return False

    if result is "id" or result == "id":
        return True
return False

Within the evaluation of Model.__getitem__, a KeyError is raised. At a fundamental level, if Model is a mapping type, I think it should implement __contains__ as well as __getitem__. The Python data model says this:

It is recommended that both mappings and sequences implement the __contains__() method to allow efficient use of the in operator; for mappings, in should search the mapping’s keys

NicholasM
  • 4,557
  • 1
  • 20
  • 47
  • I used `0` just to access the first item in the list returned from the server. I edited my question and inserted how the first item looks like when calling `print(applicationInterface.state["items"][0])`. – 5t4cktr4c3 Oct 27 '19 at 15:15