diff --git a/README.rst b/README.rst index 95a1446..f25b3b7 100644 --- a/README.rst +++ b/README.rst @@ -25,8 +25,8 @@ nocaselist - A case-insensitive list for Python Overview -------- -Class ``NocaseList`` is a case-insensitive list that preserves -the lexical case of its items. +Class `NocaseList`_ is a case-insensitive list that preserves the lexical case +of its items. Example: @@ -43,6 +43,14 @@ Example: >>> 'ALPHA' in list1 # Any lookup or comparison is case-insensitive True +The `NocaseList`_ class supports the functionality of the built-in +`list class of Python 3.8`_, so its documentation applies completely. +Methods that have been added to the built-in ``list`` class between Python 2.7 +and Python 3.8 (i.e. ``clear()`` and ``copy()``) are supported by the +`NocaseList`_ class on all Python versions it supports. + +.. _list class of Python 3.8: https://docs.python.org/3.8/library/stdtypes.html#list +.. _NocaseList: https://nocaselist.readthedocs.io/en/stable/reference.html#nocaselist.NocaseList Installation ------------ diff --git a/docs/intro.rst b/docs/intro.rst index 0ecc530..533796d 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -27,6 +27,14 @@ Example: >>> 'ALPHA' in list1 # Any lookup or comparison is case-insensitive True +The :class:`~nocaselist.NocaseList` class supports the functionality of the +built-in `list class of Python 3.8`_, so its documentation applies completely. +Methods that have been added to the built-in ``list`` class between Python 2.7 +and Python 3.8 (i.e. ``clear()`` and ``copy()``) are supported by the +:class:`~nocaselist.NocaseList` class on all Python versions it supports. + +.. _list class of Python 3.8: https://docs.python.org/3.8/library/stdtypes.html#list + .. _`Supported environments`: diff --git a/nocaselist/_nocaselist.py b/nocaselist/_nocaselist.py index 25fba85..1f357b0 100644 --- a/nocaselist/_nocaselist.py +++ b/nocaselist/_nocaselist.py @@ -40,18 +40,9 @@ class NocaseList(list): The implementation maintains a second list with the lower-cased items of the inherited list, and ensures that both lists are in sync. - The :class:`NocaseList` class supports the functionality of the built-in - `list class of Python 3.8`_, so its documentation applies completely. - Methods that have been added to the built-in :class:`py3:list` - class between Python 2.7 and Python 3.8 (i.e. :meth:`~NocaseList.clear` and - :meth:`~NocaseList.copy`) are supported by the :class:`NocaseList` class on - all Python versions. - The following documentation is provided only for explicit documentation of the case-insensitive behavior, and to indicate which methods have been implemented for maintaining the second lower-cased list. - - .. _list class of Python 3.8: https://docs.python.org/3.8/library/stdtypes.html#list """ # noqa E401 # pylint: enable=line-too-long @@ -322,16 +313,28 @@ def copy(self): Note: This method is supported on Python 2 and Python 3, even though the built-in list class only supports it on Python 3. """ - return NocaseList(self) + try: + # We first try to copy (normally cheaper than lower-casing) + new = super(NocaseList, self).copy() + # pylint: disable=protected-access + new._lc_list = self._lc_list.copy() + return new + except AttributeError: + return NocaseList(self) def clear(self): """ Remove all items from the list (and return None). - Note: This method was introduced in Python 3. + Note: This method is supported on Python 2 and Python 3, even though + the built-in list class only supports it on Python 3. """ - super(NocaseList, self).clear() - self._lc_list.clear() + try: + super(NocaseList, self).clear() + self._lc_list.clear() + except AttributeError: + del self[:] + del self._lc_list[:] def index(self, value, start=0, stop=9223372036854775807): """ diff --git a/tests/unittest/test_nocaselist.py b/tests/unittest/test_nocaselist.py index 754ff18..35126ff 100644 --- a/tests/unittest/test_nocaselist.py +++ b/tests/unittest/test_nocaselist.py @@ -23,6 +23,12 @@ if TEST_AGAINST_LIST: print("\nInfo: test_nocaselist.py tests run against standard list") +# Indicates that the list to be tested has a copy() method +LIST_HAS_COPY = not TEST_AGAINST_LIST or sys.version_info[0] == 3 + +# Indicates that the list to be tested has a clear() method +LIST_HAS_CLEAR = not TEST_AGAINST_LIST or sys.version_info[0] == 3 + # The list class being tested # pylint: disable=invalid-name NocaseList = list if TEST_AGAINST_LIST else _NocaseList @@ -749,7 +755,7 @@ def test_NocaseList_iter(testcase, nclist, exp_items): value='', exp_result=False, ), - None, None, False + None, None, True ), ( "Empty list, with non-existing non-empty value (not found)", @@ -758,7 +764,7 @@ def test_NocaseList_iter(testcase, nclist, exp_items): value='Dog', exp_result=False, ), - None, None, False + None, None, True ), # Non-empty NocaseList @@ -1774,14 +1780,14 @@ def test_NocaseList_count(testcase, nclist, value, exp_result): dict( nclist=NocaseList(), ), - None, None, True + None if LIST_HAS_COPY else AttributeError, None, True ), ( "List with two items", dict( nclist=NocaseList(['Dog', 'Cat']), ), - None, None, True + None if LIST_HAS_COPY else AttributeError, None, True ), ] @@ -1795,9 +1801,6 @@ def test_NocaseList_copy(testcase, nclist): Test function for NocaseList.copy() """ - if TEST_AGAINST_LIST: - pytest.skip("built-in list class does not support copy()") - # The code to be tested nclist_copy = nclist.copy() @@ -1828,14 +1831,14 @@ def test_NocaseList_copy(testcase, nclist): dict( nclist=NocaseList(), ), - None, None, True + None if LIST_HAS_CLEAR else AttributeError, None, True ), ( "List with two items", dict( nclist=NocaseList(['Dog', 'Cat']), ), - None, None, True + None if LIST_HAS_CLEAR else AttributeError, None, True ), ] @@ -1849,9 +1852,6 @@ def test_NocaseList_clear(testcase, nclist): Test function for NocaseList.clear() """ - if not hasattr(list, 'clear'): - pytest.skip("On this Python version, list does not have clear()") - # Don't change the testcase data, but a copy nclist_copy = NocaseList(nclist)