diff --git a/src/libsemigroups_pybind11/to.py b/src/libsemigroups_pybind11/to.py index d40d0b26..30ac41a9 100644 --- a/src/libsemigroups_pybind11/to.py +++ b/src/libsemigroups_pybind11/to.py @@ -8,7 +8,8 @@ |libsemigroups_pybind11| objects from one type to another. """ -from typing import _GenericAlias +from collections.abc import Iterator as _Iterator +from typing import Any as _Any, get_origin as _get_origin from _libsemigroups_pybind11 import ( Order as _Order, @@ -56,25 +57,26 @@ from .todd_coxeter import ToddCoxeter as _ToddCoxeter -# FIXME this function converts list[int] -> list, but shouldn't -def _nice_name(type_list): - """Convert an iterable of type-like things into a string""" - single_element = False - if not isinstance(type_list, tuple): - single_element = True - type_list = [type_list] +def _nice_name(types: _Iterator[_Any]) -> str: + """Convert an iterable of things into a string""" + not_tuple = False + if not isinstance(types, tuple): + not_tuple = True + types = (types,) out = [] - for t in type_list: + for t in types: if isinstance(t, str): - out.append(t) - elif isinstance(t, _GenericAlias): + out.append(f'"{t}"') + elif _get_origin(t) is not None: out.append(str(t)) elif hasattr(t, "__name__"): out.append(t.__name__) else: out.append(str(t)) - if single_element: + if not_tuple: return out[0] + if len(types) == 1: + out[0] += "," return f"({', '.join(out)})" diff --git a/tests/test_to.py b/tests/test_to.py index 1e50cc34..4599ae00 100644 --- a/tests/test_to.py +++ b/tests/test_to.py @@ -8,6 +8,8 @@ """This module contains some tests for the to function.""" +from typing import Literal + import pytest # TODO(1) be good to remove the imports from _libsemigroups_pybind11, but @@ -50,6 +52,7 @@ to, ) from libsemigroups_pybind11.detail.cxx_wrapper import to_cxx +from libsemigroups_pybind11.to import _nice_name ############################################################################### # Helper functions @@ -1068,6 +1071,41 @@ def test_to_Congruence_010(): ############################################################################### +@pytest.mark.parametrize( + "value, expected", + [ + (list[int], "list[int]"), + (list[str], "list[str]"), + (dict[str, list[int]], "dict[str, list[int]]"), + (tuple[int, ...], "tuple[int, ...]"), + (Literal[1, 2], "typing.Literal[1, 2]"), + (list, "list"), + (str, "str"), + (Presentation, "Presentation"), + ("Set", '"Set"'), + (Order.lenlex, "Order.lenlex"), + ((Presentation,), "(Presentation,)"), + ((Presentation, list[int]), "(Presentation, list[int])"), + ( + (KnuthBendix, list[int], "Trie", Order.lenlex), + '(KnuthBendix, list[int], "Trie", Order.lenlex)', + ), + ], +) +def test_nice_name(value, expected): + assert _nice_name(value) == expected + + +def test_to_invalid_word_type(): + with pytest.raises(TypeError) as exc_info: + to(sample_froidure_pin(), rtype=(Presentation, list[str])) + + message = str(exc_info.value) + assert "\n * (Presentation, list[int])\n" in message + assert '\n * (KnuthBendix, list[int], "Trie", Order.lenlex)\n' in message + assert message.endswith("but found: (Presentation, list[str])") + + def test_to_999(): x = 10 with pytest.raises(TypeError):