If there is only one item in thing, you implicitly convert it
to string, but not if there are multiple things which means
that itemizedList([1,2,3]) will raise TypeError but
itemizedList([1]) will not. I think you should call str on each
of the items in things.

If I pass in a string, it will itemize the characters of the
string - which is probably confusing.

If I pass a generator / iterator how should the function
behave? I don't think it should be allowed. I think you should
test "if not isinstance(things, (list, tuple)" and raise
TypeError in that case.

I went with "if not hasattr(things, 'len') or isinstance(things, basestring)", do you think that's a good way to do so?

hasattr is against the Twisted coding standard. The way to write what you meant is getattr(things, '__len__', None) is not None. Also, basestring is probably not something you ever want to use. As written, the function only works on "native strings" - bytes on Python 2, unicode on Python 3. This isn't a good idea for a new API. Pick a type and implement the function for that type. Once you've done this, it'll be easy: just reject inputs that are not of that type (and you can skip the __len__ check, I think).

Other things to note:

There's some tension between this ticket and #6341. Perhaps consider putting this functionality somewhere else - maybe in trial's util module; maybe it's even a private helper only for trial.

Use L{int} instead of C{int}. When you have the name of a thing, that's what L{} is for.

An object that has a length and can be iterated over isn't exactly a type. If there is no well-defined type for an argument, then you can leave @type off. However, I think you should just say the type is L{list} in this case.

Test method docstrings are sentences, they should end with a ".".

The list in test_notString is missing some whitespace between its elements.

Add an assertion about the text of the TypeError (and probably change the text of the TypeError after you change the documentation about the type of things).

If the API stays public, it needs to be included in the module's __all__.

There's some tension between this ticket and #6341. Perhaps consider putting this functionality somewhere else

Another way to resolve the tension would be to simply deprecate everything currently in twisted.python.text rather than the entire module. The module is being deprecated because stuff there has other (more standard?) implementations, or is quite specific.

You're right, i moved it to trial.util and made it 'private'

If it is going to live in trial, then it should be private, but see above.

(minor) Please use SynchronousTestCase, rather than TestCase, since you are testing anything asynchronous.

(tricky) I find the fact that delimiter contains a trailing space, but `finalDelimiter doesn't contain a leading or trailing space confusing. I'm not sure what to suggest here, except this should at least be documented.

It would be helpful to people using this, to give some suggestion of what to pass for finalDelimter (for example "and" and `"or" are likely choices).

The docstring isn't entirely accurate, since delimiter won't be used if there are exactly two items.

I find toPhrase is somewhat ambiguous. There are many things that could be turned into a phrase, and it is unclear which this does. Something like listToPhrase would be clearer.

It appears that there isn't any documentation on how to test the string value of an exception. After some investigation, it appears that str(error) seems to be the most sensible way. I've filed #6220 to document this, and have updated the branch to reflect this.

I also changed the news file to misc, since this doesn't intoduce any public functions.

Other than that, this looks good. I'll merge once the buildbot is done.