Repository: pydicom/pydicom

Issue:
Recursion error when reading private elements from implicit VR
**Describe the bug**
In current master
```python
from pydicom import Dataset
from pydicom.dataelem import RawDataElement
from pydicom.tag import Tag

ds = Dataset()
tag = Tag(0x70050000)
ds[tag] = RawDataElement(tag, None, 2, b'\x01\x02', 0, True, True)
print(ds)
```

Whee....
```
Traceback (most recent call last):
  File ".../pydicom/datadict.py", line 279, in get_entry
    return DicomDictionary[tag]
KeyError: (7005, 0000)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File ".../pydicom/dataelem.py", line 792, in DataElement_from_raw
    VR = dictionary_VR(raw.tag)
  File ".../pydicom/datadict.py", line 335, in dictionary_VR
    return get_entry(tag)[0]
  File ".../pydicom/datadict.py", line 285, in get_entry
    raise KeyError(f"Tag {tag} not found in DICOM dictionary")
KeyError: 'Tag (7005, 0000) not found in DICOM dictionary'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File ".../pydicom/datadict.py", line 279, in get_entry
    return DicomDictionary[tag]
KeyError: (7005, 0000)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File ".../pydicom/dataelem.py", line 792, in DataElement_from_raw
    VR = dictionary_VR(raw.tag)
  File ".../pydicom/datadict.py", line 335, in dictionary_VR
    return get_entry(tag)[0]
  File ".../pydicom/datadict.py", line 285, in get_entry
    raise KeyError(f"Tag {tag} not found in DICOM dictionary")
KeyError: 'Tag (7005, 0000) not found in DICOM dictionary'

During handling of the above exception, another exception occurred:

[snip!]

  File ".../pydicom/dataset.py", line 935, in __getitem__
    self[tag] = DataElement_from_raw(data_elem, character_set, self)
  File ".../pydicom/dataelem.py", line 800, in DataElement_from_raw
    VR = _private_vr_for_tag(dataset, raw.tag)
  File ".../pydicom/dataelem.py", line 739, in _private_vr_for_tag
    private_creator = ds.get(private_creator_tag, "")
  File ".../pydicom/dataset.py", line 768, in get
    return self.__getitem__(key)
  File ".../pydicom/dataset.py", line 935, in __getitem__
    self[tag] = DataElement_from_raw(data_elem, character_set, self)
  File ".../pydicom/dataelem.py", line 792, in DataElement_from_raw
    VR = dictionary_VR(raw.tag)
  File ".../pydicom/datadict.py", line 335, in dictionary_VR
    return get_entry(tag)[0]
  File ".../pydicom/datadict.py", line 285, in get_entry
    raise KeyError(f"Tag {tag} not found in DICOM dictionary")
RecursionError: maximum recursion depth exceeded
```

Issue occurs in dataelem._private_vr_for_tag, I think something to do with using the passed Dataset to perform the lookup that needs a lookup, etc, etc, etc. Introduced by #1316 @mrbean-bremen?

No idea how that made it through CI... I'm still kind of unsure if maybe I'm doing something stupid. But then again it works fine with v2.1.2:
```python
>>> print(ds)
(7005, 0000) Private Creator                     UN: b'\x01\x02'
```

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/pydicom/pydicom/pull/1350?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) Report
> Merging [#1350](https://codecov.io/gh/pydicom/pydicom/pull/1350?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) (69d4567) into [master](https://codecov.io/gh/pydicom/pydicom/commit/ee190caa827267c25cbdf43037d356102a45ff5e?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) (ee190ca) will **decrease** coverage by `0.12%`.
> The diff coverage is `41.17%`.

> :exclamation: Current head 69d4567 differs from pull request most recent head fa56612. Consider uploading reports for the commit fa56612 to get more accurate results
[![Impacted file tree graph](https://codecov.io/gh/pydicom/pydicom/pull/1350/graphs/tree.svg?width=650&height=150&src=pr&token=YciLV15Lwr&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom)](https://codecov.io/gh/pydicom/pydicom/pull/1350?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom)

```diff
@@            Coverage Diff             @@
##           master    #1350      +/-   ##
==========================================
- Coverage   95.29%   95.17%   -0.13%     
==========================================
  Files          60       60              
  Lines        9657     9675      +18     
==========================================
+ Hits         9203     9208       +5     
- Misses        454      467      +13     
```


| [Impacted Files](https://codecov.io/gh/pydicom/pydicom/pull/1350?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom) | Coverage Δ | |
|---|---|---|
| [pydicom/data/download.py](https://codecov.io/gh/pydicom/pydicom/pull/1350/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHlkaWNvbS9kYXRhL2Rvd25sb2FkLnB5) | `67.76% <33.33%> (-5.57%)` | :arrow_down: |
| [pydicom/data/retry.py](https://codecov.io/gh/pydicom/pydicom/pull/1350/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHlkaWNvbS9kYXRhL3JldHJ5LnB5) | `52.17% <33.33%> (-4.97%)` | :arrow_down: |
| [pydicom/dataelem.py](https://codecov.io/gh/pydicom/pydicom/pull/1350/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom#diff-cHlkaWNvbS9kYXRhZWxlbS5weQ==) | `98.13% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pydicom/pydicom/pull/1350?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pydicom/pydicom/pull/1350?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom). Last update [ee190ca...fa56612](https://codecov.io/gh/pydicom/pydicom/pull/1350?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pydicom).

mrbean-bremen: Coverage seems to be confused, will ignore it...

Patch:
diff --git a/pydicom/dataelem.py b/pydicom/dataelem.py
index f0d9344bd..a4182896d 100644
--- a/pydicom/dataelem.py
+++ b/pydicom/dataelem.py
@@ -739,9 +739,13 @@ def _private_vr_for_tag(ds: Optional["Dataset"], tag: BaseTag) -> str:
     Returns
     -------
     str
-        The VR of the private tag if found in the private dictionary or "UN".
+        "LO" if the tag is a private creator, the VR of the private tag if
+        found in the private dictionary, or "UN".
     """
-    if ds is not None:
+    if tag.is_private_creator:
+        return "LO"
+    # invalid private tags are handled as UN
+    if ds is not None and (tag.element & 0xff00):
         private_creator_tag = tag.group << 16 | (tag.element >> 8)
         private_creator = ds.get(private_creator_tag, "")
         if private_creator:
@@ -801,10 +805,7 @@ def DataElement_from_raw(
             # just read the bytes, no way to know what they mean
             if raw.tag.is_private:
                 # for VR for private tags see PS3.5, 6.2.2
-                if raw.tag.is_private_creator:
-                    VR = 'LO'
-                else:
-                    VR = _private_vr_for_tag(dataset, raw.tag)
+                VR = _private_vr_for_tag(dataset, raw.tag)
 
             # group length tag implied in versions < 3.0
             elif raw.tag.element == 0:


Test patch:
diff --git a/pydicom/tests/test_dataset.py b/pydicom/tests/test_dataset.py
index 584c8c81f..add1b7305 100644
--- a/pydicom/tests/test_dataset.py
+++ b/pydicom/tests/test_dataset.py
@@ -1204,6 +1204,19 @@ class TestDataset:
         assert len(ds) == 2
         assert ds[0x000b0010].value == 'dog^2'
 
+    def test_read_invalid_private_tag_number_as_un(self):
+        # regression test for #1347
+        ds = Dataset()
+        # not a valid private tag number nor a valid private creator
+        tag1 = Tag(0x70050000)  # this one caused a recursion
+        tag2 = Tag(0x70050005)
+        ds[tag1] = RawDataElement(tag1, None, 2, b'\x01\x02', 0, True, True)
+        ds[tag2] = RawDataElement(tag2, None, 2, b'\x03\x04', 0, True, True)
+        assert ds[tag1].value == b'\x01\x02'
+        assert ds[tag1].VR == 'UN'
+        assert ds[tag2].value == b'\x03\x04'
+        assert ds[tag2].VR == 'UN'
+
     def test_is_original_encoding(self):
         """Test Dataset.write_like_original"""
         ds = Dataset()