splitwise
splitwise copied to clipboard
updateExpense fails because User has overriden __getattr__
The User
class overrides __getattr__
to return None
(link).
I am not sure why this is, but a side effect is that when you check for the existence of magic methods, instead of receiving a real answer, it simply returns None
.
This, in turn, means that Requests is not able to encode User
objects, when called in methods like updateExpense
. This is due to this line. Due to the override, when val
is a User
object, not hasattr(val, '__iter__')
evaluates to False
, instead of returning True
like it logically should.
As a result, the User
object that is the value of created_by
is not wrapped in a list, but instead Requests tries to iterate on it directly and updateExpense
fails. I'm not sure how updateExpense
was ever able to work for anyone.
Reproduction:
my_script.py
from splitwise import Splitwise
CONSUMER_KEY = 'AAA'
CONSUMER_SECRET = 'BBB'
API_KEY = 'CCC'
sObj = Splitwise(CONSUMER_KEY, CONSUMER_SECRET, api_key=API_KEY)
group = sObj.getGroups()[0]
expense = sObj.getExpenses(group_id=group.id)[0]
expense.setDescription('My New Description')
sObj.updateExpense(e)
Console
$ python -m my_script
Traceback (most recent call last):
File "/Users/eppercut/miniconda3/lib/python3.7/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/Users/eppercut/miniconda3/lib/python3.7/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/Users/eppercut/Documents/expenses/scripts/my_script.py", line 38, in <module>
sObj.updateExpense(e)
File "/Users/eppercut/.local/share/virtualenvs/scripts-G3O7iiDh/src/splitwise/splitwise/__init__.py", line 619, in updateExpense
Splitwise.UPDATE_EXPENSE_URL+"/"+str(expense_id), "POST", expense_data, files=files)
File "/Users/eppercut/.local/share/virtualenvs/scripts-G3O7iiDh/src/splitwise/splitwise/__init__.py", line 271, in __makeRequest
prep_req = requestObj.prepare()
File "/Users/eppercut/.local/share/virtualenvs/scripts-G3O7iiDh/lib/python3.7/site-packages/requests/models.py", line 269, in prepare
hooks=self.hooks,
File "/Users/eppercut/.local/share/virtualenvs/scripts-G3O7iiDh/lib/python3.7/site-packages/requests/models.py", line 321, in prepare
self.prepare_body(data, files, json)
File "/Users/eppercut/.local/share/virtualenvs/scripts-G3O7iiDh/lib/python3.7/site-packages/requests/models.py", line 517, in prepare_body
body = self._encode_params(data)
File "/Users/eppercut/.local/share/virtualenvs/scripts-G3O7iiDh/lib/python3.7/site-packages/requests/models.py", line 102, in _encode_params
for v in vs:
TypeError: 'User' object is not iterable
Value of expense_data
when it is POSTed via __makeRequest
(line)
{
'group_id': 123,
'description':'My New Description',
'repeats': False,
'repeat_interval': None,
'email_reminder': False,
'email_reminder_in_advance': -1,
'next_repeat': None,
'details': None,
'comments_count': 0,
'payment': False,
'creation_method': None,
'transaction_method': 'offline',
'transaction_confirmed': False,
'cost': '42.0',
'currency_code': 'USD',
'created_by': <splitwise.user.User object at 0x7f944bc0b438>,
'date': '2022-01-01T01:00:00Z',
'created_at': '2022-01-01T01:00:00Z',
'updated_at': '2022-01-01T01:00:00Z',
'deleted_at': None, 'receipt': <splitwise.receipt.Receipt object at 0x7f944bc0b4a9>,
'category': <splitwise.category.Category object at 0x7f944bc0b4e1>,
'updated_by': None,
'deleted_by': None,
'friendship_id': None,
'expense_bundle_id': None,
'repayments': [<splitwise.debt.Debt object at 0x7f944bc0b551>],
'transaction_id': None,
'users__0__first_name': 'Alice',
'users__0__last_name': None,
'users__0__user_id': 456,
'users__0__email': None,
'users__0__registration_status': None,
'users__0__picture': <splitwise.picture.Picture object at 0x7f944bc0b5c1>,
'users__0__paid_share': '42.0',
'users__0__owed_share': '0.0',
'users__0__net_balance': '42.0',
'users__1__first_name': 'Bob',
'users__1__last_name': None,
'users__1__user_id': 789,
'users__1__email': None,
'users__1__registration_status': None,
'users__1__picture': <splitwise.picture.Picture object at 0x7f944bc0b631>,
'users__1__paid_share': '0.0',
'users__1__owed_share': '42.0',
'users__1__net_balance': '-42.0',
'category_id': 1
}
The problem is the User
object which is the value of created_by
, since as explained above, Requests cannot properly check if it is iterable.
I am not sure why User
and Expense
classes override __getattr__
and none of the other classes do. However, it seems like the solution would be to at least not override properly checking for magic methods.
@eppercut - does https://github.com/eppercut/splitwise/tree/fix-attrs resolve this enough?
Well this is one problem yes, but we would need to resolve all the objects here including repayments, receipt etc. I will have a look at it. Splitwise update API says that you should only send the fields you want to update.
So for now, you can create a new expense object from the expense object you get from the server.
Hi @namaggarwal. I am having the same issue TypeError: 'User' object is not iterable
with both updateExpense and createExpense methods. What I wanted to do is to update the percentage of the repayments for several of my expenses, do it manually would take very long. And I thought to do it by updating the existing expenses or by deleting them and then creating new ones, with the correct values. Is there a workaround that i am not seeing it? Thanks!
I am releasing a fix for it soon. Splitwise API has stopped accepting adhoc params, so I deleted other params and also added a test
Fixed in v3.0.0
@ocamposfaria You would need to calculate the owed_share and paid_share and then update it for all the expenses
@namaggarwal I did as you said! Thanks for your help, I really appreciate the effort :)