Repository: pydicom/pynetdicom

Issue:
Test failures due to upstream pydicom changes
[For example](https://github.com/pydicom/pynetdicom/runs/5578106440?check_suite_focus=true)

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/pydicom/pynetdicom/pull/760?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) Report
> Merging [#760](https://codecov.io/gh/pydicom/pynetdicom/pull/760?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) (aae8ec4) into [master](https://codecov.io/gh/pydicom/pynetdicom/commit/5cfd10b6e4cf05be3a6c5dad84d7a21fb9164966?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) (5cfd10b) will **decrease** coverage by `0.15%`.
> The diff coverage is `n/a`.

```diff
@@             Coverage Diff             @@
##            master     #760      +/-   ##
===========================================
- Coverage   100.00%   99.84%   -0.16%     
===========================================
  Files           28       28              
  Lines         8648     8648              
===========================================
- Hits          8648     8635      -13     
- Misses           0       13      +13     
```

| [Impacted Files](https://codecov.io/gh/pydicom/pynetdicom/pull/760?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) | Coverage Δ | |
|---|---|---|
| [pynetdicom/utils.py](https://codecov.io/gh/pydicom/pynetdicom/pull/760/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHluZXRkaWNvbS91dGlscy5weQ==) | `88.79% <0.00%> (-11.21%)` | :arrow_down: |


Patch:
diff --git a/pynetdicom/apps/qrscp/db.py b/pynetdicom/apps/qrscp/db.py
index faab2a4d6..9d89ec9f7 100644
--- a/pynetdicom/apps/qrscp/db.py
+++ b/pynetdicom/apps/qrscp/db.py
@@ -605,6 +605,9 @@ def _search_uid_list(elem, session, query=None):
     if not query:
         query = session.query(Instance)
 
+    if elem.VM == 1:
+        return query.filter(attr == elem.value)
+
     return query.filter(attr.in_(elem.value))
 
 
diff --git a/pynetdicom/dimse_primitives.py b/pynetdicom/dimse_primitives.py
index 607624216..daa100bb9 100644
--- a/pynetdicom/dimse_primitives.py
+++ b/pynetdicom/dimse_primitives.py
@@ -6,7 +6,7 @@ Notes:
     in order for the DIMSE messages/primitives to be created correctly.
 """
 
-from collections.abc import MutableSequence
+from collections.abc import Sequence
 from io import BytesIO
 import logging
 from pathlib import Path
@@ -67,7 +67,7 @@ class DIMSEPrimitive:
     _action_type_id: Optional[int] = None
     _affected_sop_class_uid: Optional[UID] = None
     _affected_sop_instance_uid: Optional[UID] = None
-    _attribute_identifier_list: Optional[List[BaseTag]] = None
+    _attribute_identifier_list: Union[None, BaseTag, List[BaseTag]] = None
     _dataset: Optional[BytesIO] = None
     _event_type_id: Optional[int] = None
     _message_id: Optional[int] = None
@@ -1502,16 +1502,17 @@ class N_GET(DIMSEPrimitive):
         self._AffectedSOPInstanceUID = value  # type: ignore
 
     @property
-    def AttributeIdentifierList(self) -> Optional[List[BaseTag]]:
+    def AttributeIdentifierList(self) -> Optional[Union[BaseTag, List[BaseTag]]]:
         """Get or set the *Attribute Identifier List* as a :class:`list` of
         :class:`~pydicom.tag.BaseTag`.
 
         Parameters
         ----------
-        list of pydicom.tag.BaseTag
+        pydicom.tag.BaseTag or list of pydicom.tag.BaseTag
             The value to use for the *Attribute Identifier List* parameter.
-            A list of pydicom :class:`pydicom.tag.BaseTag` instances or any
-            values acceptable for creating them.
+            A pydicom :class:`pydicom.tag.BaseTag` or list of
+            :class:`pydicom.tag.BaseTag` or any values acceptable for creating
+            them.
         """
         return self._attribute_identifier_list
 
@@ -1520,24 +1521,28 @@ class N_GET(DIMSEPrimitive):
         self, value: Optional[Union[BaseTag, List[BaseTag]]]
     ) -> None:
         """Set the *Attribute Identifier List*."""
+        # Be careful as a tag value of 0x00000000 is possible (though unlikely)
         if value is None:
             self._attribute_identifier_list = None
             return
 
-        # Singleton tags get put in a list
-        if not isinstance(value, (list, MutableSequence)):
-            value = [value]
-
-        # Empty list -> None
-        if not value:
-            self._attribute_identifier_list = None
-            return
-
         try:
-            # Convert each item in list to pydicom Tag
-            self._attribute_identifier_list = [Tag(tag) for tag in value]
+            if isinstance(value, Sequence):
+                if not value:
+                    self._attribute_identifier_list = None
+
+                if len(value) == 1:
+                    # Force to a single value - workaround for pydicom #757
+                    self._attribute_identifier_list = Tag(value[0])
+                else:
+                    self._attribute_identifier_list = [Tag(tag) for tag in value]
+            else:
+                self._attribute_identifier_list = Tag(value)
         except (TypeError, ValueError):
-            raise ValueError("Attribute Identifier List must be a list of pydicom Tags")
+            raise ValueError(
+                "Attribute Identifier List must be convertible to a pydicom "
+                "BaseTag or list of BaseTag"
+            )
 
     @property
     def AttributeList(self) -> Optional[BytesIO]:
diff --git a/pynetdicom/events.py b/pynetdicom/events.py
index 0bf7eb3f3..57b1e2fc0 100644
--- a/pynetdicom/events.py
+++ b/pynetdicom/events.py
@@ -549,6 +549,9 @@ class Event:
             if attr_list is None:
                 return []
 
+            if not isinstance(attr_list, list):
+                return [attr_list]
+
             return attr_list
         except AttributeError:
             pass


Test patch:
diff --git a/pynetdicom/tests/test_ae.py b/pynetdicom/tests/test_ae.py
index 997b0cbbb..ff628b288 100644
--- a/pynetdicom/tests/test_ae.py
+++ b/pynetdicom/tests/test_ae.py
@@ -8,7 +8,7 @@ import time
 
 import pytest
 
-from pydicom import read_file
+from pydicom import read_file, config as PYD_CONFIG
 from pydicom.dataset import Dataset
 from pydicom.uid import UID, ImplicitVRLittleEndian
 
@@ -29,6 +29,10 @@ from pynetdicom.sop_class import RTImageStorage, Verification
 from pynetdicom.transport import AssociationServer, RequestHandler
 
 
+if hasattr(PYD_CONFIG, "settings"):
+    PYD_CONFIG.settings.reading_validation_mode = 0
+
+
 # debug_logger()
 
 
@@ -606,6 +610,7 @@ class TestAEGoodAssociation:
             msgs = [
                 "TCP Initialisation Error: timed out",
                 "TCP Initialisation Error: [Errno -2] Name or service not known",
+                # "TCP Initialisation Error: [Errno 113] No route to host",
             ]
             assert len([m for m in msgs if m in caplog.text]) == 1
 
diff --git a/pynetdicom/tests/test_assoc_n.py b/pynetdicom/tests/test_assoc_n.py
index 8b304d469..0bf620e5f 100644
--- a/pynetdicom/tests/test_assoc_n.py
+++ b/pynetdicom/tests/test_assoc_n.py
@@ -1059,7 +1059,7 @@ class TestAssociationSendNGet:
             ds.Status = 0xFFF0
             ds.ErrorComment = "Some comment"
             ds.ErrorID = 12
-            ds.AttributeIdentifierList = [0x00100020]
+            ds.AttributeIdentifierList = 0x00100020
             return ds, None
 
         self.ae = ae = AE()
@@ -1081,7 +1081,7 @@ class TestAssociationSendNGet:
         assert status.Status == 0xFFF0
         assert status.ErrorComment == "Some comment"
         assert status.ErrorID == 12
-        assert status.AttributeIdentifierList == [0x00100020]
+        assert status.AttributeIdentifierList == 0x00100020
         assert ds is None
         assoc.release()
         assert assoc.is_released
diff --git a/pynetdicom/tests/test_dimse_n.py b/pynetdicom/tests/test_dimse_n.py
index 3ae23ccf2..c0c191dbc 100644
--- a/pynetdicom/tests/test_dimse_n.py
+++ b/pynetdicom/tests/test_dimse_n.py
@@ -9,9 +9,10 @@ import logging
 
 import pytest
 
+from pydicom import config as PYD_CONFIG
 from pydicom.dataset import Dataset
 from pydicom.dataelem import DataElement
-from pydicom.tag import Tag
+from pydicom.tag import Tag, BaseTag
 from pydicom.uid import UID
 
 from pynetdicom import _config
@@ -63,6 +64,11 @@ from .encoded_dimse_n_msg import (
     n_create_rsp_ds,
 )
 
+
+if hasattr(PYD_CONFIG, "settings"):
+    PYD_CONFIG.settings.reading_validation_mode = 0
+
+
 LOGGER = logging.getLogger("pynetdicom")
 LOGGER.setLevel(logging.CRITICAL)
 
@@ -375,14 +381,14 @@ class TestPrimitive_N_GET:
             Tag(0x7FE0, 0x0010),
         ] == primitive.AttributeIdentifierList
         primitive.AttributeIdentifierList = [(0x7FE0, 0x0010)]
-        assert [Tag(0x7FE0, 0x0010)] == primitive.AttributeIdentifierList
+        assert Tag(0x7FE0, 0x0010) == primitive.AttributeIdentifierList
         primitive.AttributeIdentifierList = (0x7FE0, 0x0010)
-        assert [Tag(0x7FE0, 0x0010)] == primitive.AttributeIdentifierList
+        assert Tag(0x7FE0, 0x0010) == primitive.AttributeIdentifierList
 
-        elem = DataElement((0x0000, 0x0005), "AT", [Tag(0x0000, 0x1000)])
-        assert isinstance(elem.value, MutableSequence)
+        elem = DataElement((0x0000, 0x0005), "AT", Tag(0x0000, 0x1000))
+        assert isinstance(elem.value, BaseTag)
         primitive.AttributeIdentifierList = elem.value
-        assert [Tag(0x0000, 0x1000)] == primitive.AttributeIdentifierList
+        assert Tag(0x0000, 0x1000) == primitive.AttributeIdentifierList
 
         # MessageID
         primitive.MessageID = 11
diff --git a/pynetdicom/tests/test_events.py b/pynetdicom/tests/test_events.py
index 3b7503b6b..082a8d339 100644
--- a/pynetdicom/tests/test_events.py
+++ b/pynetdicom/tests/test_events.py
@@ -479,6 +479,14 @@ class TestEvent:
         assert isinstance(tags[1], BaseTag)
         assert tags[1] == 0x00100020
 
+        request.AttributeIdentifierList = 0x00100010
+        event = Event(
+            None, evt.EVT_N_GET, {"request": request, "context": self.context.as_tuple}
+        )
+
+        tags = event.attribute_identifiers
+        assert tags == [0x00100010]
+
     def test_action_type(self):
         """Test with action_type."""
         request = N_ACTION()
diff --git a/pynetdicom/tests/test_pdu_items.py b/pynetdicom/tests/test_pdu_items.py
index 68a20ca6c..4d42ddefc 100644
--- a/pynetdicom/tests/test_pdu_items.py
+++ b/pynetdicom/tests/test_pdu_items.py
@@ -6,6 +6,7 @@ import sys
 
 import pytest
 
+from pydicom import config as PYD_CONFIG
 from pydicom.uid import UID
 
 from pynetdicom import _config, debug_logger
@@ -85,6 +86,10 @@ from .encoded_pdu_items import (
 )
 
 
+if hasattr(PYD_CONFIG, "settings"):
+    PYD_CONFIG.settings.reading_validation_mode = 0
+
+
 # debug_logger()
 
 
diff --git a/pynetdicom/tests/test_primitives.py b/pynetdicom/tests/test_primitives.py
index 53afce7dd..fe4dd91ba 100644
--- a/pynetdicom/tests/test_primitives.py
+++ b/pynetdicom/tests/test_primitives.py
@@ -4,6 +4,7 @@ import logging
 
 import pytest
 
+from pydicom import config as PYD_CONFIG
 from pydicom.uid import UID
 
 from pynetdicom import _config
@@ -26,6 +27,11 @@ from pynetdicom.pdu_primitives import (
 from pynetdicom.presentation import PresentationContext
 from pynetdicom.utils import pretty_bytes
 
+
+if hasattr(PYD_CONFIG, "settings"):
+    PYD_CONFIG.settings.reading_validation_mode = 0
+
+
 LOGGER = logging.getLogger("pynetdicom")
 LOGGER.setLevel(logging.CRITICAL)
 
diff --git a/pynetdicom/tests/test_validators.py b/pynetdicom/tests/test_validators.py
index 207ee3161..e79371c9f 100644
--- a/pynetdicom/tests/test_validators.py
+++ b/pynetdicom/tests/test_validators.py
@@ -2,11 +2,16 @@
 
 import pytest
 
+from pydicom import config as PYD_CONFIG
 
 from pynetdicom import _config
 from pynetdicom._validators import validate_ae, validate_ui
 
 
+if hasattr(PYD_CONFIG, "settings"):
+    PYD_CONFIG.settings.reading_validation_mode = 0
+
+
 AE_REFERENCE = [
     ("", True, ""),
     ("A", True, ""),