Repository: pydicom/pynetdicom
Pull request #679 (2021-09-08 08:09:51)

[MRG] Fix LGTM recommendations
Fix some of these alerts:
https://lgtm.com/projects/g/pydicom/pynetdicom/?mode=list&severity=recommendation

#### Tasks
- [ ] Unit tests added that reproduce issue or prove feature is working
- [X] Fix or feature added
- [ ] Documentation and examples updated (if relevant)
- [ ] Unit tests passing and coverage at 100% after adding fix/feature
- [ ] Type annotations updated and passing with mypy
- [ ] Apps updated and tested (if relevant)


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

```diff
@@             Coverage Diff             @@
##            master     #679      +/-   ##
===========================================
- Coverage   100.00%   99.84%   -0.16%     
===========================================
  Files           28       28              
  Lines         8620     8611       -9     
===========================================
- Hits          8620     8598      -22     
- Misses           0       13      +13     
```

| [Impacted Files](https://codecov.io/gh/pydicom/pynetdicom/pull/679?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) | Coverage Δ | |
|---|---|---|
| [pynetdicom/dimse\_primitives.py](https://codecov.io/gh/pydicom/pynetdicom/pull/679/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHluZXRkaWNvbS9kaW1zZV9wcmltaXRpdmVzLnB5) | `100.00% <ø> (ø)` | |
| [pynetdicom/pdu.py](https://codecov.io/gh/pydicom/pynetdicom/pull/679/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHluZXRkaWNvbS9wZHUucHk=) | `100.00% <ø> (ø)` | |
| [pynetdicom/pdu\_items.py](https://codecov.io/gh/pydicom/pynetdicom/pull/679/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHluZXRkaWNvbS9wZHVfaXRlbXMucHk=) | `100.00% <ø> (ø)` | |
| [pynetdicom/pdu\_primitives.py](https://codecov.io/gh/pydicom/pynetdicom/pull/679/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHluZXRkaWNvbS9wZHVfcHJpbWl0aXZlcy5weQ==) | `100.00% <ø> (ø)` | |
| [pynetdicom/utils.py](https://codecov.io/gh/pydicom/pynetdicom/pull/679/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHluZXRkaWNvbS91dGlscy5weQ==) | `89.16% <ø> (-10.84%)` | :arrow_down: |
| [pynetdicom/\_validators.py](https://codecov.io/gh/pydicom/pynetdicom/pull/679/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHluZXRkaWNvbS9fdmFsaWRhdG9ycy5weQ==) | `100.00% <100.00%> (ø)` | |
| [pynetdicom/transport.py](https://codecov.io/gh/pydicom/pynetdicom/pull/679/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHluZXRkaWNvbS90cmFuc3BvcnQucHk=) | `100.00% <100.00%> (ø)` | |


Patch:
diff --git a/.github/workflows/pr-linter.yml b/.github/workflows/pr-linter.yml
index cf021ecb4..971ad44ce 100644
--- a/.github/workflows/pr-linter.yml
+++ b/.github/workflows/pr-linter.yml
@@ -37,7 +37,9 @@ jobs:
     - name: Install dependencies
       run: |
         python -m pip install --upgrade pip
-        pip install flake8
+        pip install flake8 black
+    - name: Check code formatting
+      run: black . --check
     - name: Check for syntax errors
       run: python -m flake8 --select=E901,E999,F821,F822,F823 ${{ steps.pydiff.outputs.pyfiles }} --ignore F821
     - name: Check for style warnings
diff --git a/docs/changelog/v2.0.0.rst b/docs/changelog/v2.0.0.rst
index b356a2079..e973cc70a 100644
--- a/docs/changelog/v2.0.0.rst
+++ b/docs/changelog/v2.0.0.rst
@@ -9,8 +9,8 @@ The major breaking changes with the version 2.0 release are:
   * SOP class variable names now use their `new DICOM UID keywords
     <https://dicom.nema.org/medical/dicom/current/output/chtml/part06/chapter_A.html#table_A-1>`_
     (for example, *VerificationSOPClass* becomes simply *Verification*)
-  * AE titles values should be :class:`str` rather than :class:`bytes` and no
-    longer contain any trailing padding bytes
+  * AE titles values should be :class:`str` rather than :class:`bytes` and will no
+    longer contain any trailing padding spaces
 
 
 Fixes
diff --git a/docs/reference/sop_classes.rst b/docs/reference/sop_classes.rst
index 4ebbe2fe4..71a70997e 100644
--- a/docs/reference/sop_classes.rst
+++ b/docs/reference/sop_classes.rst
@@ -128,6 +128,7 @@ Non-patient Object
    ImplantAssemblyTemplateStorage
    ImplantTemplateGroupStorage
    ProtocolApprovalStorage
+   XADefinedProcedureProtocolStorage
 
 Print Management
 ................
@@ -280,6 +281,7 @@ Storage
    LensometryMeasurementsStorage
    MacularGridThicknessAndVolumeReportStorage
    MammographyCADSRStorage
+   MicroscopyBulkSimpleAnnotationsStorage
    MRImageStorage
    MRSpectroscopyStorage
    MultichannelRespiratoryWaveformStorage
@@ -323,9 +325,11 @@ Storage
    RTPlanStorage
    RTRadiationRecordSetStorage
    RTRadiationSalvageRecordStorage
+   RTRadiationSetDeliveryInstructionStorage
    RTRadiationSetStorage
    RTSegmentAnnotationStorage
    RTStructureSetStorage
+   RTTreatmentPreparationStorage
    RTTreatmentSummaryRecordStorage
    SecondaryCaptureImageStorage
    SegmentationStorage
@@ -358,6 +362,7 @@ Storage
    VolumeRenderingVolumetricPresentationStateStorage
    WideFieldOphthalmicPhotography3DCoordinatesImageStorage
    WideFieldOphthalmicPhotographyStereographicProjectionImageStorage
+   XAPerformedProcedureProtocolStorage
    XAXRFGrayscaleSoftcopyPresentationStateStorage
    XRay3DAngiographicImageStorage
    XRay3DCraniofacialImageStorage
diff --git a/pynetdicom/_validators.py b/pynetdicom/_validators.py
index 355006cad..eb508d4bd 100644
--- a/pynetdicom/_validators.py
+++ b/pynetdicom/_validators.py
@@ -1,11 +1,9 @@
 """Validation functions used by pynetdicom"""
 
-from collections import OrderedDict
 import logging
-from typing import Union, Dict, Optional, cast, Tuple
+from typing import Tuple
 import unicodedata
 
-from pydicom.dataset import Dataset
 from pydicom.uid import UID
 
 
diff --git a/pynetdicom/apps/common.py b/pynetdicom/apps/common.py
index c731d26c6..957c26291 100644
--- a/pynetdicom/apps/common.py
+++ b/pynetdicom/apps/common.py
@@ -421,7 +421,6 @@ class ElementPath:
         ]
         _int = ["SL", "SS", "SV", "UL", "US", "UV"]
         _float = ["FD", "FL"]
-        _byte = ["OB", "OD", "OF", "OL", "OW", "OV", "UN"]
         # Try to convert value to appropriate type
         if self.VR == "AT" and "\\" in value:
             value = value.split("\\")
@@ -627,7 +626,7 @@ def handle_store(event, args, app_logger):
             with open(filename, "wb") as f:
                 f.write(b"\x00" * 128)
                 f.write(b"DICM")
-                f.write(write_file_meta_info(f, event.file_meta))
+                write_file_meta_info(f, event.file_meta)
                 f.write(encode(ds, False, True, True))
         else:
             # We use `write_like_original=False` to ensure that a compliant
diff --git a/pynetdicom/apps/echoscu/echoscu.py b/pynetdicom/apps/echoscu/echoscu.py
index 9da684d5d..2a8175b23 100755
--- a/pynetdicom/apps/echoscu/echoscu.py
+++ b/pynetdicom/apps/echoscu/echoscu.py
@@ -221,8 +221,7 @@ def main(args=None):
     # If we successfully associated then send C-ECHO
     if assoc.is_established:
         for ii in range(args.repeat):
-            # `status` is a pydicom Dataset
-            status = assoc.send_c_echo()
+            assoc.send_c_echo()
 
         # Abort or release association
         if args.abort:
diff --git a/pynetdicom/apps/findscu/findscu.py b/pynetdicom/apps/findscu/findscu.py
index b74c5c1d6..91e36c115 100755
--- a/pynetdicom/apps/findscu/findscu.py
+++ b/pynetdicom/apps/findscu/findscu.py
@@ -6,10 +6,8 @@ requests to a QR/BWM - Find SCP.
 """
 
 import argparse
-import os
 import sys
 
-from pydicom import dcmread
 from pydicom.dataset import Dataset
 from pydicom.uid import (
     ExplicitVRLittleEndian,
@@ -297,7 +295,6 @@ def main(args=None):
         identifier = create_dataset(args, APP_LOGGER)
     except Exception as exc:
         APP_LOGGER.exception(exc)
-        raise exc
         sys.exit(1)
 
     # Create application entity
diff --git a/pynetdicom/apps/getscu/getscu.py b/pynetdicom/apps/getscu/getscu.py
index 14c1314b9..beb9c0026 100755
--- a/pynetdicom/apps/getscu/getscu.py
+++ b/pynetdicom/apps/getscu/getscu.py
@@ -5,10 +5,8 @@ For sending Query/Retrieve (QR) C-GET requests to a QR Get SCP.
 """
 
 import argparse
-import os
 import sys
 
-from pydicom.dataset import Dataset
 from pydicom.uid import (
     ExplicitVRLittleEndian,
     ImplicitVRLittleEndian,
diff --git a/pynetdicom/apps/movescu/movescu.py b/pynetdicom/apps/movescu/movescu.py
index 0dfd54113..071328f89 100755
--- a/pynetdicom/apps/movescu/movescu.py
+++ b/pynetdicom/apps/movescu/movescu.py
@@ -5,11 +5,8 @@ For sending Query/Retrieve (QR) C-MOVE requests to a QR Move SCP.
 """
 
 import argparse
-import os
 import sys
 
-from pydicom.dataset import Dataset
-
 from pynetdicom import (
     AE,
     evt,
diff --git a/pynetdicom/apps/qrscp/handlers.py b/pynetdicom/apps/qrscp/handlers.py
index a24d3f7f1..f91288b12 100644
--- a/pynetdicom/apps/qrscp/handlers.py
+++ b/pynetdicom/apps/qrscp/handlers.py
@@ -310,7 +310,6 @@ def handle_store(event, storage_dir, db_path, cli_config, logger):
     # Try and add the instance to the database
     #   If we fail then don't even try to store
     fpath = os.path.join(storage_dir, sop_instance)
-    db_dir = os.path.dirname(db_path)
 
     if os.path.exists(fpath):
         logger.warning("Instance already exists in storage directory, overwriting")
diff --git a/pynetdicom/apps/qrscp/qrscp.py b/pynetdicom/apps/qrscp/qrscp.py
index 591da10a6..ce231c522 100755
--- a/pynetdicom/apps/qrscp/qrscp.py
+++ b/pynetdicom/apps/qrscp/qrscp.py
@@ -7,7 +7,6 @@ import os
 import sys
 
 import pydicom.config
-from pydicom.dataset import Dataset
 
 from pynetdicom import AE, evt, AllStoragePresentationContexts, ALL_TRANSFER_SYNTAXES
 from pynetdicom import _config, _handlers
diff --git a/pynetdicom/apps/storescp/storescp.py b/pynetdicom/apps/storescp/storescp.py
index c0d5998ff..75e626ca3 100755
--- a/pynetdicom/apps/storescp/storescp.py
+++ b/pynetdicom/apps/storescp/storescp.py
@@ -5,10 +5,8 @@ Used for receiving DICOM SOP Instances transferred from an SCU.
 """
 
 import argparse
-import os
 import sys
 
-from pydicom.dataset import Dataset
 from pydicom.uid import (
     ExplicitVRLittleEndian,
     ImplicitVRLittleEndian,
diff --git a/pynetdicom/dimse_messages.py b/pynetdicom/dimse_messages.py
index 19fb6476a..3ab384bac 100644
--- a/pynetdicom/dimse_messages.py
+++ b/pynetdicom/dimse_messages.py
@@ -5,11 +5,11 @@ import logging
 from math import ceil
 from pathlib import Path
 from tempfile import NamedTemporaryFile
-from typing import Iterator, Optional, TYPE_CHECKING, cast, Union, Tuple, Type, List
+from typing import Iterator, Optional, TYPE_CHECKING, cast, Union, Tuple
 
 from pydicom.dataset import Dataset
 from pydicom.filewriter import write_file_meta_info
-from pydicom.tag import Tag, BaseTag
+from pydicom.tag import Tag
 from pydicom.uid import UID
 
 from pynetdicom import _config
diff --git a/pynetdicom/dimse_primitives.py b/pynetdicom/dimse_primitives.py
index 8bc17ecb6..58a0b50c7 100644
--- a/pynetdicom/dimse_primitives.py
+++ b/pynetdicom/dimse_primitives.py
@@ -6,7 +6,6 @@ Notes:
     in order for the DIMSE messages/primitives to be created correctly.
 """
 
-import codecs
 from collections.abc import MutableSequence
 from io import BytesIO
 import logging
@@ -17,7 +16,6 @@ import warnings
 from pydicom.tag import Tag, BaseTag
 from pydicom.uid import UID
 
-from pynetdicom import _config
 from pynetdicom._globals import OptionalUIDType
 from pynetdicom.utils import set_ae, decode_bytes, set_uid
 
diff --git a/pynetdicom/pdu.py b/pynetdicom/pdu.py
index 5421e20b8..978b5c0ad 100644
--- a/pynetdicom/pdu.py
+++ b/pynetdicom/pdu.py
@@ -19,7 +19,6 @@ There are seven different PDUs:
                     to_primitive               decode
 """
 
-import codecs
 import logging
 from struct import Struct
 from typing import (
diff --git a/pynetdicom/pdu_items.py b/pynetdicom/pdu_items.py
index 8e0408c2f..ad8a36acf 100644
--- a/pynetdicom/pdu_items.py
+++ b/pynetdicom/pdu_items.py
@@ -40,7 +40,6 @@
 - PresentationDataValueItem
 """
 
-import codecs
 import logging
 from struct import Struct
 from typing import (
diff --git a/pynetdicom/pdu_primitives.py b/pynetdicom/pdu_primitives.py
index 05f27c9b0..2355ca026 100644
--- a/pynetdicom/pdu_primitives.py
+++ b/pynetdicom/pdu_primitives.py
@@ -1,7 +1,6 @@
 """
 Implementaion of the service parameter primitives.
 """
-import codecs
 import logging
 from typing import Optional, List, Any, Union, Tuple, cast, Type
 
diff --git a/pynetdicom/presentation.py b/pynetdicom/presentation.py
index e31c94cf8..b5c0234ad 100644
--- a/pynetdicom/presentation.py
+++ b/pynetdicom/presentation.py
@@ -459,10 +459,10 @@ class PresentationContext:
             s.append(f"Abstract Syntax: {self.abstract_syntax.name}")
 
         s.append("Transfer Syntax(es):")
-        for syntax in self.transfer_syntax:
-            s.append(f"    ={syntax.name}")
-        else:
+        if not self.transfer_syntax:
             s.append("    (none)")
+        else:
+            s.extend(f"    ={ts.name}" for ts in self.transfer_syntax)
 
         if self.result is not None:
             s.append(f"Result: {self.status}")
diff --git a/pynetdicom/transport.py b/pynetdicom/transport.py
index e01b08a1d..3404480bd 100644
--- a/pynetdicom/transport.py
+++ b/pynetdicom/transport.py
@@ -268,15 +268,13 @@ class AssociationSocket:
             address.
         """
         # Solution from https://stackoverflow.com/a/28950776
-        temp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-        try:
-            # We use `host` to allow unit testing
-            temp.connect(host)
-            addr: str = temp.getsockname()[0]
-        except:
-            addr = "127.0.0.1"
-        finally:
-            temp.close()
+        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as temp:
+            try:
+                # We use `host` to allow unit testing
+                temp.connect(host)
+                addr: str = temp.getsockname()[0]
+            except:
+                addr = "127.0.0.1"
 
         return addr
 
diff --git a/pynetdicom/utils.py b/pynetdicom/utils.py
index 6a4460247..dd250f7af 100644
--- a/pynetdicom/utils.py
+++ b/pynetdicom/utils.py
@@ -13,11 +13,8 @@ from typing import (
     Union,
     cast,
     Callable,
-    Tuple,
-    Type,
     Sequence,
 )
-import unicodedata
 
 try:
     import ctypes
@@ -147,15 +144,12 @@ def pretty_bytes(
         byte_count += len(chunk)
         gen = (format(x, "02x") for x in chunk)
 
-        if max_size is not None and byte_count <= max_size:
+        if max_size is None or byte_count <= max_size:
             line = prefix + delimiter.join(gen)
             lines.append(line + suffix)
-        elif max_size is not None and byte_count > max_size:
+        else:
             cutoff_output = True
             break
-        else:
-            line = prefix + delimiter.join(gen)
-            lines.append(line + suffix)
 
     if cutoff_output:
         lines.insert(0, prefix + f"Only dumping {max_size} bytes.")
diff --git a/setup.py b/setup.py
index 036be8902..349c066df 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,5 @@
 from setuptools import setup, find_packages
 from pathlib import Path
-import sys
 
 
 BASE_DIR = Path(__file__).parent


Test patch:
diff --git a/pynetdicom/apps/tests/test_storescp.py b/pynetdicom/apps/tests/test_storescp.py
index b4db5dbb0..03a0b3b18 100644
--- a/pynetdicom/apps/tests/test_storescp.py
+++ b/pynetdicom/apps/tests/test_storescp.py
@@ -2,6 +2,7 @@
 
 import logging
 import os
+from pathlib import Path
 import shutil
 import subprocess
 import sys
@@ -18,16 +19,22 @@ from pydicom.uid import (
 )
 
 from pynetdicom import AE, evt, debug_logger, DEFAULT_TRANSFER_SYNTAXES
-from pynetdicom.sop_class import Verification, CTImageStorage
+from pynetdicom.sop_class import (
+    Verification,
+    CTImageStorage,
+    SecondaryCaptureImageStorage,
+)
 
 
 # debug_logger()
 
 
-APP_DIR = os.path.join(os.path.dirname(__file__), "../")
-APP_FILE = os.path.join(APP_DIR, "storescp", "storescp.py")
-DATA_DIR = os.path.join(APP_DIR, "../", "tests", "dicom_files")
-DATASET_FILE = os.path.join(DATA_DIR, "CTImageStorage.dcm")
+APP_DIR = Path(__file__).parent.parent
+APP_FILE = APP_DIR / "storescp" / "storescp.py"
+DATA_DIR = APP_DIR.parent / "tests" / "dicom_files"
+DATASET_FILE = DATA_DIR / "CTImageStorage.dcm"
+DEFLATED_FILE = DATA_DIR / "SCImageStorage_Deflated.dcm"
+TEST_DIR = Path(__file__).parent / "test_dir"
 
 
 def start_storescp(args):
@@ -53,6 +60,9 @@ class StoreSCPBase:
 
     def teardown(self):
         """Clear any active threads"""
+        if TEST_DIR.exists():
+            shutil.rmtree(os.fspath(TEST_DIR))
+
         if self.ae:
             self.ae.shutdown()
 
@@ -310,9 +320,9 @@ class StoreSCPBase:
         ae.add_requested_context(Verification)
         ae.add_requested_context(CTImageStorage)
 
-        assert "test_dir" not in os.listdir()
+        assert not TEST_DIR.exists()
 
-        self.p = p = self.func(["-od", "test_dir"])
+        self.p = p = self.func(["-od", os.fspath(TEST_DIR)])
         time.sleep(0.5)
 
         ds = dcmread(DATASET_FILE)
@@ -323,9 +333,9 @@ class StoreSCPBase:
         assert status.Status == 0x0000
         assoc.release()
 
-        assert "CT.{}".format(ds.SOPInstanceUID) in os.listdir("test_dir")
-        shutil.rmtree("test_dir")
-        assert "test_dir" not in os.listdir()
+        assert (TEST_DIR / f"CT.{ds.SOPInstanceUID}").exists()
+        shutil.rmtree(os.fspath(TEST_DIR))
+        assert not TEST_DIR.exists()
 
     def test_flag_ignore(self):
         """Test the --ignore flag."""
@@ -347,7 +357,38 @@ class StoreSCPBase:
         assert status.Status == 0x0000
         assoc.release()
 
-        assert "CT.{}".format(ds.SOPInstanceUID) not in os.listdir()
+        assert not (TEST_DIR / f"CT.{ds.SOPInstanceUID}").exists()
+
+    def test_store_deflated(self):
+        """Test storing deflated dataset"""
+        self.ae = ae = AE()
+        ae.acse_timeout = 5
+        ae.dimse_timeout = 5
+        ae.network_timeout = 5
+        ae.add_requested_context(
+            SecondaryCaptureImageStorage,
+            DeflatedExplicitVRLittleEndian,
+        )
+
+        assert not TEST_DIR.exists()
+
+        self.p = p = self.func(["-od", os.fspath(TEST_DIR)])
+        time.sleep(0.5)
+
+        ds = dcmread(DEFLATED_FILE)
+
+        assoc = ae.associate("localhost", 11112)
+        assert assoc.is_established
+        status = assoc.send_c_store(ds)
+        assert status.Status == 0x0000
+        assoc.release()
+
+        p = TEST_DIR / f"SC.{ds.SOPInstanceUID}"
+        assert p.exists()
+        ds = dcmread(p)
+        assert ds.file_meta.TransferSyntaxUID == DeflatedExplicitVRLittleEndian
+        shutil.rmtree(os.fspath(TEST_DIR))
+        assert not TEST_DIR.exists()
 
 
 class TestStoreSCP(StoreSCPBase):
diff --git a/pynetdicom/tests/test_presentation.py b/pynetdicom/tests/test_presentation.py
index f0e143745..bc9fa5cc1 100644
--- a/pynetdicom/tests/test_presentation.py
+++ b/pynetdicom/tests/test_presentation.py
@@ -217,23 +217,36 @@ class TestPresentationContext:
         pc._scp_role = True
         pc._scu_role = False
         pc.result = 0x02
-        assert "1.1.1" in pc.__str__()
-        assert "Implicit" in pc.__str__()
-        assert "Provider Rejected" in pc.__str__()
+        out = [
+            "ID: 1",
+            "Abstract Syntax: 1.1.1",
+            "Transfer Syntax(es):",
+            "    =Implicit VR Little Endian",
+            "    =1.2.3",
+            "Result: Provider Rejected",
+        ]
+        assert "\n".join(out) == str(pc)
 
         pc._as_scu = True
         pc._as_scp = False
-        assert "Role: SCU only" in pc.__str__()
+        assert "Role: SCU only" in str(pc)
         pc._as_scp = True
-        assert "Role: SCU and SCP" in pc.__str__()
+        assert "Role: SCU and SCP" in str(pc)
         pc._as_scu = False
-        assert "Role: SCP only" in pc.__str__()
+        assert "Role: SCP only" in str(pc)
         pc._as_scp = False
-        assert "Role: (none)" in pc.__str__()
+        assert "Role: (none)" in str(pc)
 
         pc._transfer_syntax = []
-        print(pc)
-        assert "    (none)" in pc.__str__()
+        out = [
+            "ID: 1",
+            "Abstract Syntax: 1.1.1",
+            "Transfer Syntax(es):",
+            "    (none)",
+            "Result: Provider Rejected",
+            "Role: (none)",
+        ]
+        assert "\n".join(out) == str(pc)
 
     def test_context_id(self):
         """Test setting context_id."""
diff --git a/pynetdicom/tests/test_utils.py b/pynetdicom/tests/test_utils.py
index fee3b940d..e0779894d 100644
--- a/pynetdicom/tests/test_utils.py
+++ b/pynetdicom/tests/test_utils.py
@@ -146,7 +146,12 @@ class TestPrettyBytes:
             bytestream, prefix="", delimiter="", items_per_line=10, max_size=100
         )
         assert len(result) == 11  # 10 plus the cutoff line
+        assert "Only dumping 100 bytes." == result[0]
+        assert "382e332e312e312e3120" == result[-1]
+
         result = pretty_bytes(bytestream, max_size=None)
+        assert "Only dumping 100 bytes." != result[0]
+        assert "  43  4f  4d  5f  30  39  30" == result[-1]
 
         # suffix
         result = pretty_bytes(bytestream, suffix="xxx")