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



Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/pydicom/pydicom/pull/2158?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 98.38%. Comparing base [(`5f47958`)](https://app.codecov.io/gh/pydicom/pydicom/commit/5f479582edb560e4d983293ad39f65181e2ff78a?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) to head [(`484edd2`)](https://app.codecov.io/gh/pydicom/pydicom/commit/484edd29b046912928b5dec8023b6cb41201b040?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #2158      +/-   ##
==========================================
+ Coverage   98.34%   98.38%   +0.04%     
==========================================
  Files          77       77              
  Lines       13452    13458       +6     
==========================================
+ Hits        13229    13241      +12     
+ Misses        223      217       -6     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/pydicom/pydicom/pull/2158?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom).


Patch:
diff --git a/doc/release_notes/v3.1.0.rst b/doc/release_notes/v3.1.0.rst
index 6ba7c38e5..c9430b807 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 f2414711a..e7abffd17 100644
--- a/src/pydicom/dataset.py
+++ b/src/pydicom/dataset.py
@@ -395,8 +395,6 @@ class Dataset:
         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 @@ class 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 0b68a8bff..efd5ce403 100644
--- a/tests/test_dataset.py
+++ b/tests/test_dataset.py
@@ -1739,6 +1739,23 @@ class TestDataset:
         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 @@ CAMEL_CASE = (
         "patient_records",
         "_parent_encoding",
         "_dict",
-        "is_decompressed",
         "read_encoding",
         "_private_blocks",
         "default_element_format",