So maybe the solution really is to have a character type -- then a
string is a sequence of characters, rather that a sequence of string,
and all these problems go away.

If a character object has all the methods of a string except indexing
(and iterating) it might even be mostly backward compatible....

Otherwise, special casing in multiple cases may be the only option.

Or maybe all we need is a way to spell "any iterable of strings except
a string", though that would only help type checking.

I agree, the fact that a string consists of strings has bothered me as well.  It makes some things work well but some recursive algorithms requires special checks.  There should be a way to iterate over a string as characters.

AtomicIterable seems a good idea, but maybe it could be just the conceptual starting point of building up a class hierarchy of "iterability", and such a class could be passed to the 'flatten' method proposed above in the thread by Nick Coghlan.

Some conceptual inspiration may be taken from numpy where one can have 0-dimensional arrays which are different from scalars - and different from n-D array with 1 element (n > 0)

In [7]: x = np.ndarray(())

In [8]: x
Out[8]: array(21.0)

In [9]: type(x)
Out[9]: numpy.ndarray

In [10]: x[()]
Out[10]: 21.0

In [11]: np.asscalar(x)
Out[11]: 21.0

 In [13]: x = np.ndarray((1,))

In [14]: x
Out[14]: array([ 17.5])