Repository: omni-us/jsonargparse

Issue:
jsonargparse._util._parse_value_or_config
<!-- If you like this project, please ⭐ star it https://github.com/omni-us/jsonargparse/ -->

## 🐛 Bug report

I'm using LightningCLI, and not sure if this is due to Lightning or jsonargparse, but...

Specifying the value of a variable as the path to a  `.csv` file loads the content of the csv file when parsing arguments instead of giving the path as a string. 

### To reproduce

minimal_lightning.py
```python
from __future__ import annotations

import os
import os.path

import lightning as L
import numpy as np
import torch
from lightning.pytorch.cli import LightningCLI


class MinimalModel(L.LightningModule):
    def __init__(self):
        super().__init__()
        self.layer = torch.nn.Linear(1, 1)


class MinimalDataModule(L.LightningDataModule):
    def __init__(self, data: np.ndarray | os.PathLike | str):
        super().__init__()
        if isinstance(data, os.PathLike | str):
            self.data = torch.load(data)

        elif isinstance(data, np.ndarray):
            self.data = torch.from_numpy(data)

        self.targets = torch.zeros_like(self.data)


def main() -> None:
    LightningCLI(
        MinimalModel,
        MinimalDataModule,
    )


if __name__ == "__main__":
    main()
```

minimal_lightning.yml
```yaml
data:
  data: "~/code/transformertf/minimal_lightning.csv"
```

minimal_lightning.csv
```
1
2
3
4
```

Run as `python lightning_minimal.py fit -c minimal_lightning.yml`

### Expected behavior

The classes should be instantiated by LightningCLI/jsonargparse and the `data` argument to the DataModule should be a path, and not the loaded and give 

```
Traceback (most recent call last):
  File "/home/anton/code/transformertf/minimal_lightning.py", line 42, in <module>
    main()
  File "/home/anton/code/transformertf/minimal_lightning.py", line 35, in main
    LightningCLI(
  File "/home/anton/.conda/envs/acc-py/lib/python3.11/site-packages/lightning/pytorch/cli.py", line 385, in __init__
    self.instantiate_classes()
  File "/home/anton/.conda/envs/acc-py/lib/python3.11/site-packages/lightning/pytorch/cli.py", line 535, in instantiate_classes
    self.config_init = self.parser.instantiate_classes(self.config)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/anton/.conda/envs/acc-py/lib/python3.11/site-packages/jsonargparse/_deprecated.py", line 141, in patched_instantiate_classes
    cfg = self._unpatched_instantiate_classes(cfg, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/anton/.conda/envs/acc-py/lib/python3.11/site-packages/jsonargparse/_core.py", line 1191, in instantiate_classes
    cfg[subcommand] = subparser.instantiate_classes(cfg[subcommand], instantiate_groups=instantiate_groups)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/anton/.conda/envs/acc-py/lib/python3.11/site-packages/jsonargparse/_deprecated.py", line 141, in patched_instantiate_classes
    cfg = self._unpatched_instantiate_classes(cfg, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/anton/.conda/envs/acc-py/lib/python3.11/site-packages/jsonargparse/_core.py", line 1185, in instantiate_classes
    component.instantiate_class(component, cfg)
  File "/home/anton/.conda/envs/acc-py/lib/python3.11/site-packages/jsonargparse/_signatures.py", line 588, in group_instantiate_class
    parent[key] = instantiator_fn(group.group_class, **value)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/anton/.conda/envs/acc-py/lib/python3.11/site-packages/jsonargparse/_common.py", line 148, in default_class_instantiator
    return class_type(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/anton/code/transformertf/minimal_lightning.py", line 22, in __init__
    self.data = torch.load(data)
                ^^^^^^^^^^^^^^^^
  File "/home/anton/.conda/envs/acc-py/lib/python3.11/site-packages/torch/serialization.py", line 997, in load
    with _open_file_like(f, 'rb') as opened_file:
         ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/anton/.conda/envs/acc-py/lib/python3.11/site-packages/torch/serialization.py", line 444, in _open_file_like
    return _open_file(name_or_buffer, mode)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/anton/.conda/envs/acc-py/lib/python3.11/site-packages/torch/serialization.py", line 425, in __init__
    super().__init__(open(name, mode))
                     ^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: '1\n2\n3\n4\n'

```

### Environment

<!-- Fill in the list below. -->

- jsonargparse version (e.g., 4.8.0):
- Python version (e.g., 3.9):
- How jsonargparse was installed (e.g. `pip install jsonargparse[all]`):
- OS (e.g., Linux):

jsonargparse: 4.29.0, installed with `lightning[pytorch-extra]`
omegaconf: 2.3.0
lightning: 2.2.4
torch: 2.3.0
Python: 3.11.5

Arch Linux


Discussion:
sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=omni-us_jsonargparse&pullRequest=518) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=518&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=518&resolutions=WONTFIX)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=omni-us_jsonargparse&pullRequest=518&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/no-data-16px.png '') No data about Coverage  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=omni-us_jsonargparse&pullRequest=518&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=omni-us_jsonargparse&pullRequest=518)


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 100.00%. Comparing base [(`5e627a2`)](https://app.codecov.io/gh/omni-us/jsonargparse/commit/5e627a20d5432c76ecd704ff04070f806df27a1f?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) to head [(`7a2d967`)](https://app.codecov.io/gh/omni-us/jsonargparse/commit/7a2d967a62fdbafcea9a85a38de8a8a709cdc8e2?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us).


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


```diff
@@            Coverage Diff            @@
##              main      #518   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           21        21           
  Lines         6172      6176    +4     
=========================================
+ Hits          6172      6176    +4     
```

| [Flag](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | Coverage Δ | |
|---|---|---|
| [py3.10](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `87.33% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.10_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.63% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.10_pydantic1](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `49.28% <100.00%> (+0.06%)` | :arrow_up: |
| [py3.10_pydantic2](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `49.06% <100.00%> (+0.06%)` | :arrow_up: |
| [py3.10_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.65% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.11](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `87.31% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.11_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.63% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.11_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.65% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.12](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `87.46% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.12_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.63% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.12_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.65% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.7](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `87.84% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.7_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.21% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.7_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.24% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.8](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `88.02% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.8_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.38% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.8_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.40% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.9](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `87.88% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.9_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.27% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.9_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.28% <100.00%> (+<0.01%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us#carryforward-flags-in-the-pull-request-comment) to find out more.


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/omni-us/jsonargparse/pull/518?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us).   
: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=omni-us).


Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 5172ae0..1f93c04 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -23,6 +23,12 @@ Added
   (`lightning#13613
   <https://github.com/Lightning-AI/pytorch-lightning/discussions/13613>`__)
 
+Fixed
+^^^^^
+- Parsing incorrectly provides file content when type is a union with a
+  subclass, PathLike and string (`#516
+  <https://github.com/omni-us/jsonargparse/issues/516>`__).
+
 
 v4.29.0 (2024-05-24)
 --------------------
diff --git a/jsonargparse/_typehints.py b/jsonargparse/_typehints.py
index 1e28eca..fee45a7 100644
--- a/jsonargparse/_typehints.py
+++ b/jsonargparse/_typehints.py
@@ -198,7 +198,7 @@ class ActionTypeHint(Action):
                     subtypes = tuple(t for t, s in zip(typehint.__args__, subtype_supported) if s)
                     typehint = Union[subtypes]  # type: ignore[assignment]
             self._typehint = typehint
-            self._enable_path = False if is_optional(typehint, Path) else enable_path
+            self._enable_path = False if is_pathlike(typehint) else enable_path
         elif "_typehint" not in kwargs:
             raise ValueError("Expected typehint keyword argument.")
         else:
@@ -647,6 +647,12 @@ class ActionTypeHint(Action):
             return argcomplete_warn_redraw_prompt(prefix, msg)
 
 
+def is_pathlike(typehint) -> bool:
+    if get_typehint_origin(typehint) == Union:
+        return any(is_pathlike(t) for t in typehint.__args__)
+    return is_subclass(typehint, os.PathLike)
+
+
 def raise_unexpected_value(message: str, val: Any = inspect._empty, exception: Optional[Exception] = None) -> NoReturn:
     if val is not inspect._empty:
         message += f". Got value: {val}"


Test patch:
diff --git a/jsonargparse_tests/test_paths.py b/jsonargparse_tests/test_paths.py
index 81adecd..614267f 100644
--- a/jsonargparse_tests/test_paths.py
+++ b/jsonargparse_tests/test_paths.py
@@ -4,7 +4,7 @@ import json
 import os
 from calendar import Calendar
 from pathlib import Path
-from typing import Any, Dict, List, Optional
+from typing import Any, Dict, List, Optional, Union
 
 import pytest
 
@@ -237,17 +237,44 @@ def test_enable_path_list_path_fr_default_stdin(parser, tmp_cwd, mock_stdin, sub
         assert ["file1", "file2"] == [str(x) for x in cfg.list]
 
 
-class Data:
+class DataOptionalPath:
     def __init__(self, path: Optional[os.PathLike] = None):
         pass
 
 
-def test_enable_path_os_pathlike_subclass_parameter(parser, tmp_cwd):
+def test_enable_path_optional_pathlike_subclass_parameter(parser, tmp_cwd):
     data_path = Path("data.json")
     data_path.write_text('{"a": 1}')
 
-    parser.add_argument("--data", type=Data, enable_path=True)
+    parser.add_argument("--data", type=DataOptionalPath, enable_path=True)
 
-    cfg = parser.parse_args([f"--data={__name__}.Data", f"--data.path={data_path}"])
-    assert cfg.data.class_path == f"{__name__}.Data"
+    cfg = parser.parse_args([f"--data={__name__}.DataOptionalPath", f"--data.path={data_path}"])
+    assert cfg.data.class_path == f"{__name__}.DataOptionalPath"
     assert cfg.data.init_args == Namespace(path=str(data_path))
+
+
+class Base:
+    pass
+
+
+class DataUnionPath:
+    def __init__(self, path: Union[Base, os.PathLike, str] = ""):
+        pass
+
+
+def test_sub_configs_union_subclass_and_pathlike(parser, tmp_cwd):
+    data_path = Path("data.csv")
+    data_path.write_text("x\ny\n")
+    config = {
+        "data": {
+            "path": "data.csv",
+        }
+    }
+    config_path = Path("config.json")
+    config_path.write_text(json.dumps(config))
+
+    parser.add_class_arguments(DataUnionPath, "data", sub_configs=True)
+    parser.add_argument("--cfg", action="config")
+
+    cfg = parser.parse_args([f"--cfg={config_path}"])
+    assert cfg.data.path == str(data_path)