Repository: evennia/evennia

Issue:
[BUG] iter_to_str() misprint with two objects.
#### Describe the bug
<!--(Replace with a clear and concise description of what the bug is.)-->
`iter_to_str` inside `get_display_*`'s behaviour seems faulty.

With it's default separators (`sep","` , `endsep=", and"`), and 2 objects in the iterable, the in-game game return is `" and "` separating the names `(obj1 and obj2)`. While more than 2, returns `", and"`; `(obj1, obj2, and obj3)`.

Changing the default  `sep` to something like `sep="-"`, makes the return `X, and Y` as opposed to `X and Y` when there's 2 objects.

Apparenty, this happens whenever `sep` and `endsep` are the same (or `endsep` starts with the same character).
Got the same behavior with `[sep=',', endsep=',']`, `[sep='-', endsep='-']`, `[sep=';', endsep=';']`.
In these cases output with 2 objects is `X Y` as opposed to `X, Y`, for example.

#### To Reproduce
Steps to reproduce the behavior:
1. Overload any `get_display_*` function in an object.
2. Set it's `*_names` value's `iter_to_str` separator parameters as the same character. (e.g.: `sep","` , `endsep=","`)
3. Run the game and `look` in a place where there's two of anything (`Exits`, `Characters` or `Things`)
4. Check `look` output for misprint

#### Expected behavior
<!--(Replace with a clear and concise description of what you expected to happen.)-->
The presence of 2 objects in the iterable being used by `iter_to_str` should output the proper `endsep` string.
With the same separators parameters, output with 2 objects should be `X, Y`, not `X Y`.

#### Environment, Evennia version, OS etc
<!--(Replace with info. If unsure, run `evennia -v` or get the first few lines of the `about` command in-game.)-->
    Evennia 1.0.2
    OS: nt

#### Additional context
<!--(Replace with any other context about the problem, or ideas on how to solve.)-->



Discussion:
Ohh, this is with only *two* objects. Removing the default leading comma in a list of two items is not a bug, it's intended design. The default behavior of `iter_to_str` is to combine a list of items into a single correctly-punctuated natural language list, using the Oxford comma syntax. e.g for lists of 1, 2 and 3 items with the default , you should expect:

- "1"
- "1 and 2"
- "1, 2, and 3"

That said, specifying endsep="," _should_ result in a list of "1, 2" rather than "1 2", so that behavior does need to be corrected. I'll take point on it this week once I have an opportunity, since I know what the logic for that is and should be able to correct it.
I imagined it was intended, as `1 and 2` does make more sense than `1, and 2`. What started puzzling me was specifying `sep`(to something other than `","`) and leaving `endsep` defaulted; That makes a list of two objects `1, and 2`, that boggled me for a while. Started thinking this was intended behavior, heh.

Investigating further, the issue seems to be in `iter_to_str()` definition in `utils.utils`.
More specifically: 
```
  if endsep:
      if endsep.startswith(sep):
          # oxford comma alternative
          endsep = endsep[1:] if len_iter < 3 else endsep
          [...]
```
I'm currently digging a bit deeper, will keep this comment updated.

## UPDATE:

Culprit confirmed it seems.
> (Knowing I _probably shouldn't_) I started investigating the issue by doing some hard-coding on `iter_to_str()` itself inside evennia, to see it anything changed at all. Not a great idea, game showed no reaction to my edits. So...

I made my own `utils` inside `\mygame\utils` path, copied `iter_to_str()`, renamed it, removed the `if endsep.startswith(sep):` block and used that one instead, when appropriate. It breaks the Oxford Comma styling, but it works if all you need is `1, 2,[...]`.

Still trying to figure out a way to make it work as _actually_ intended.

Resolving patch:
diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py
--- a/evennia/utils/utils.py
+++ b/evennia/utils/utils.py
@@ -448,7 +448,7 @@ def iter_to_str(iterable, sep=",", endsep=", and", addquote=False):
         iterable = tuple(str(val) for val in iterable)
 
     if endsep:
-        if endsep.startswith(sep):
+        if endsep.startswith(sep) and endsep != sep:
             # oxford comma alternative
             endsep = endsep[1:] if len_iter < 3 else endsep
         elif endsep[0] not in punctuation:


Test patch:
diff --git a/evennia/utils/tests/test_utils.py b/evennia/utils/tests/test_utils.py
--- a/evennia/utils/tests/test_utils.py
+++ b/evennia/utils/tests/test_utils.py
@@ -93,6 +93,7 @@ def test_list_to_string(self):
             '"1", "2" and "3"', utils.list_to_string([1, 2, 3], endsep="and", addquote=True)
         )
         self.assertEqual("1 and 2", utils.list_to_string([1, 2]))
+        self.assertEqual("1, 2", utils.list_to_string([1, 2], endsep=","))
 
 
 class TestMLen(TestCase):