Any way to distinguish `obj[x, y]` from `obj[(x, y)]`?
Short of trying to inspect the calling source code (which is extremely fragile, with all sorts of failure cases, nowhere near worth the instability and debugging headache), this is not possible.
obj[a, b] and obj[(a, b)] mean exactly the same thing in Python. There is no semantic difference, no difference in how they are executed, and nothing to hook into to distinguish them. It'd be like trying to distinguish the whitespace in obj[a,b] and obj[a, b].
So, I want to implement a class that holds nested data. I would like to implement __getitem__ in a way so that obj[x][y] can be abbreviated by obj[x, y].
However, I noticed a problem: The signature of __getitem__ is that it expects a single positional argument instead of *args. If multiple arguments are given, they are automatically put into a tuple.
I.e. obj[a, b] and obj[(a, b)] both appear to be equivalent to obj.__getitem__((a,b))
But then how can I distinguish the two cases

The outer layer is indexed by tuples and obj[(a, b)] should return the value at that index
The outer layer is not indexed by tuples and obj[a, b] should return obj[a][b]

The only possible solutions I am aware of currently are

Abandon the idea of coercing obj[x, y] into obj[x][y]
If we only want obj[x] always write obj[x,] instead.

Both are not really satisfactory.