Repository: pydicom/pydicom

Issue:
decompress raises 'The dataset is already uncompressed' when ds.is_decompressed is False
**Describe the bug**
unexpected exception raised with code as below: 'ValueError: The dataset is already uncompressed'.

Reading an already decompressed dicom has flag ds.is_decompressed set to False
```
import pydicom
input_file = pydicom.data.get_testdata_file('MR_small.dcm')
ds = pydicom.dcmread(input_file)
# ds.is_decompressed is unexpectedly False
if not ds.is_decompressed:  
   ds.decompress()
```

**Expected behavior**
The code above would not raise an exception.

1. `ds.is_decompressed` has expected value 'True'
2. decompress never raises the exception, but has same behaviour as pydicom 2.x: it does nothing if data already is decompressed.


**Steps To Reproduce**
See above.

**Your environment**
Please use the `pydicom.env_info` module (available in **pydicom 2 or later**)
to gather information about your environment and paste it into the issue:

```bash
$ python -m pydicom.env_info
```
module       | version
------       | -------
platform     | Linux-6.11.2-arch1-1-x86_64-with-glibc2.40
Python       | 3.11.9 (main, Apr 29 2024, 06:28:25) [GCC 13.2.1 20240417]
pydicom      | 3.0.1
gdcm         | _module not found_
jpeg_ls      | _module not found_
numpy        | 2.1.1
PIL          | _module not found_
pylibjpeg    | 2.0.1
openjpeg     | 2.3.0
libjpeg      | 2.2.0

Hints from issue comments:
@mrbean-bremen: `Dataset.is_decompressed` looks like a leftover from the previous implementation - it is not documented and never updated.
I guess it makes sense to re-implement it as property in `FileDataset` that relies on `UID.is_compressed`.


Solution patch:
diff --git a/doc/release_notes/v3.1.0.rst b/doc/release_notes/v3.1.0.rst
index 6ba7c38e5d..c9430b8076 100644
--- a/doc/release_notes/v3.1.0.rst
+++ b/doc/release_notes/v3.1.0.rst
@@ -14,3 +14,6 @@ Fixes
 Enhancements
 ------------
 * Added the option to pass a ``bool`` ndarray to :func:`~pydicom.pixels.set_pixel_data` to store with *Bits Allocated* of ``1`` using bit-packing (:issue:`2141`).
+* Added the :attr:`Dataset.is_decompressed<pydicom.dataset.Dataset.is_decompressed>`
+  convenience property for determining whether a dataset uses a compressed transfer
+  syntax or not (:issue:`2155`).
diff --git a/src/pydicom/dataset.py b/src/pydicom/dataset.py
index f2414711a3..e7abffd17e 100644
--- a/src/pydicom/dataset.py
+++ b/src/pydicom/dataset.py
@@ -395,8 +395,6 @@ def __init__(self, *args: _DatasetType, **kwargs: Any) -> None:
         else:
             self._dict = args[0]
 
-        self.is_decompressed = False
-
         # the following read_XXX attributes are used internally to store
         # the properties of the dataset after read from a file
         # set depending on the endianness of the read dataset
@@ -1303,6 +1301,25 @@ def _dataset_slice(self, slce: slice) -> "Dataset":
 
         return ds
 
+    @property
+    def is_decompressed(self) -> bool:
+        """Return ``True`` if the dataset uses an uncompressed (native) transfer syntax,
+        ``False`` otherwise.
+
+        .. versionadded:: 3.1
+        """
+
+        file_meta = getattr(self, "file_meta", {})
+        tsyntax = file_meta.get("TransferSyntaxUID", "")
+        if not tsyntax:
+            raise AttributeError(
+                "Unable to determine the dataset's compression state as there's no "
+                "(0002,0010) 'Transfer Syntax UID' element in the dataset's "
+                "'file_meta' or no 'file_meta' has been set"
+            )
+
+        return not tsyntax.is_compressed
+
     @property
     def is_implicit_VR(self) -> bool | None:
         """Get/set the VR method used when encoding the dataset.


Test patch:
diff --git a/tests/test_dataset.py b/tests/test_dataset.py
index 0b68a8bffe..efd5ce4037 100644
--- a/tests/test_dataset.py
+++ b/tests/test_dataset.py
@@ -1739,6 +1739,23 @@ def test_update_raw(self):
         ds.update_raw_element(0x00100010, value=b"Bar")
         assert ds.PatientName == "Bar"
 
+    def test_is_decompressed(self):
+        """Test Dataset.is_decompressed"""
+        ds = Dataset()
+        msg = (
+            "Unable to determine the dataset's compression state as there's no "
+            r"\(0002,0010\) 'Transfer Syntax UID' element in the dataset's "
+            "'file_meta' or no 'file_meta' has been set"
+        )
+        with pytest.raises(AttributeError, match=msg):
+            ds.is_decompressed
+
+        ds.file_meta = FileMetaDataset()
+        ds.file_meta.TransferSyntaxUID = ExplicitVRLittleEndian
+        assert ds.is_decompressed is True
+        ds.file_meta.TransferSyntaxUID = JPEGBaseline8Bit
+        assert ds.is_decompressed is False
+
 
 class TestDatasetSaveAs:
     def test_no_transfer_syntax(self):
@@ -2739,7 +2756,6 @@ def contains_warn():
         "patient_records",
         "_parent_encoding",
         "_dict",
-        "is_decompressed",
         "read_encoding",
         "_private_blocks",
         "default_element_format",