Repository: Lightning-Universe/lightning-flash

Issue:
Update jsonargparse[signatures] requirement from <=4.9.0,>4.0.0 to >4.0.0,<=4.21.1 in /requirements
Updates the requirements on [jsonargparse[signatures]](https://github.com/omni-us/jsonargparse) to permit the latest version.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/omni-us/jsonargparse/blob/master/CHANGELOG.rst">jsonargparse[signatures]'s changelog</a>.</em></p>
<blockquote>
<h2>v4.21.1 (2023-05-09)</h2>
<p>Fixed
^^^^^</p>
<ul>
<li>AST resolver not working for dict used in a method when the dict is created
using the curly braces syntax.</li>
<li>Failure on multiple deep arguments linked on instantiation (<code>[#275](https://github.com/omni-us/jsonargparse/issues/275) &lt;https://github.com/omni-us/jsonargparse/issues/275&gt;</code>__).</li>
</ul>
<h2>v4.21.0 (2023-04-21)</h2>
<p>Added
^^^^^</p>
<ul>
<li>Support for dataclasses nested in a type (<code>[#243](https://github.com/omni-us/jsonargparse/issues/243) &lt;https://github.com/omni-us/jsonargparse/issues/243&gt;</code>__).</li>
<li>Support for pydantic <code>models &lt;https://docs.pydantic.dev/usage/models/&gt;</code>__ and
attrs <code>define &lt;https://www.attrs.org/en/stable/examples.html&gt;</code>__ similar to
dataclasses.</li>
<li>Support for <code>pydantic types &lt;https://docs.pydantic.dev/usage/types/#pydantic-types&gt;</code>__.</li>
<li>Backport type stubs in python&lt;=3.9 to support PEP <code>585 &lt;https://peps.python.org/pep-0585/&gt;</code>__ and <code>604 &lt;https://peps.python.org/pep-0604/&gt;</code>__ syntax.</li>
</ul>
<p>Fixed
^^^^^</p>
<ul>
<li><code>str</code> parameter in subclass incorrectly parsed as dict with implicit <code>null</code>
value (<code>[#262](https://github.com/omni-us/jsonargparse/issues/262) &lt;https://github.com/omni-us/jsonargparse/issues/262&gt;</code>__).</li>
<li>Wrong error indentation for subclass in union (<code>lightning#17254 &lt;https://github.com/Lightning-AI/lightning/issues/17254&gt;</code>__).</li>
<li><code>dataclass</code> from pydantic not working (<code>[#100](https://github.com/omni-us/jsonargparse/issues/100) comment &lt;https://github.com/omni-us/jsonargparse/issues/100#issuecomment-1408413796&gt;</code>__).</li>
<li><code>add_dataclass_arguments</code> not forwarding <code>sub_configs</code> parameter.</li>
<li>Failure to instantiate nested class group without arguments (<code>lightning#17263 &lt;https://github.com/Lightning-AI/lightning/issues/17263&gt;</code>__).</li>
</ul>
<p>Changed
^^^^^^^</p>
<ul>
<li>Switched from <code>setup.cfg</code> to <code>pyproject.toml</code> for configuration.</li>
<li>Removed <code>build_sphinx</code> from <code>setup.py</code> and documented how to build.</li>
<li>Include enum members in error when invalid value is given
(<code>lightning#17247 &lt;https://github.com/Lightning-AI/lightning/issues/17247&gt;</code>__).</li>
<li>The <code>signatures</code> extras now installs the <code>typing-extensions</code> package on
python&lt;=3.9.</li>
<li><code>CLI</code> now when given a class without methods, the class instance is
returned.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/omni-us/jsonargparse/commit/0942fb9320f6448d0c1297d4689a35c937152f61"><code>0942fb9</code></a> Bump version: 4.21.0 → 4.21.1</li>
<li><a href="https://github.com/omni-us/jsonargparse/commit/677a0f9303ad6ce878a82224ac1feec39a997de4"><code>677a0f9</code></a> [pre-commit.ci] auto fixes from pre-commit.com hooks</li>
<li><a href="https://github.com/omni-us/jsonargparse/commit/37e9cbf498b26632b828267a1985a2fb455a8538"><code>37e9cbf</code></a> Refactored util tests using pytest.</li>
<li><a href="https://github.com/omni-us/jsonargparse/commit/f3f74d18a979ba9abffccf3373619ffcd5a992f1"><code>f3f74d1</code></a> adding black formatting (<a href="https://redirect.github.com/omni-us/jsonargparse/issues/279">#279</a>)</li>
<li><a href="https://github.com/omni-us/jsonargparse/commit/1c303a69c012856e3550a0f5ed8ad0f7cad453a3"><code>1c303a6</code></a> Fixed failure on multiple deep arguments linked on instantiation (<a href="https://redirect.github.com/omni-us/jsonargparse/issues/275">#275</a>).</li>
<li><a href="https://github.com/omni-us/jsonargparse/commit/d7e6dde09dc03e5efad14c711c40a1ca7882e919"><code>d7e6dde</code></a> add Ruff as code check (<a href="https://redirect.github.com/omni-us/jsonargparse/issues/280">#280</a>)</li>
<li><a href="https://github.com/omni-us/jsonargparse/commit/8d57577b4a2b40565e59186120ef523816d59e3a"><code>8d57577</code></a> Refactored loaders_dumpers, namespace and optionals tests using pytest.</li>
<li><a href="https://github.com/omni-us/jsonargparse/commit/54bf8406a94929560bff4ea4d347a3b15750df35"><code>54bf840</code></a> Refactored formatters, jsonschema and jsonnet tests using pytest.</li>
<li><a href="https://github.com/omni-us/jsonargparse/commit/08054352698b5b55280b10a4dfd07b40859240de"><code>0805435</code></a> Refactored actions, argcomplete and cli tests using pytest.</li>
<li><a href="https://github.com/omni-us/jsonargparse/commit/9039750b4997022968ab6c841d66843199155261"><code>9039750</code></a> Removed some unittest specific code.</li>
<li>Additional commits viewable in <a href="https://github.com/omni-us/jsonargparse/compare/v4.0.1...v4.21.1">compare view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Discussion:
@dependabot rebase
## [Codecov](https://app.codecov.io/gh/Lightning-Universe/lightning-flash/pull/1564?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Lightning-Universe) Report
> Merging [#1564](https://app.codecov.io/gh/Lightning-Universe/lightning-flash/pull/1564?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Lightning-Universe) (0f0bde6) into [master](https://app.codecov.io/gh/Lightning-Universe/lightning-flash/commit/84e5e1d0431c240b6a73b6ee2accfde9f109935f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Lightning-Universe) (84e5e1d) will **decrease** coverage by `18%`.
> The diff coverage is `100%`.

> :exclamation: Current head 0f0bde6 differs from pull request most recent head 340d57c. Consider uploading reports for the commit 340d57c to get more accurate results

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


```diff
@@           Coverage Diff            @@
##           master   #1564     +/-   ##
========================================
- Coverage      86%     68%    -18%     
========================================
  Files         291     291             
  Lines       12874   12865      -9     
========================================
- Hits        11011    8731   -2280     
- Misses       1863    4134   +2271     
```

</details>
@mauvilsa could you pls advise how best to resolve this issue in the latest `jsonargparse`? :rabbit: 
A newer version of jsonargparse[signatures] exists, but since this PR has been edited by someone other than Dependabot I haven't updated it. You'll get a PR for the updated version as normal once this PR is merged.

Resolving patch:
diff --git a/src/flash/core/utilities/flash_cli.py b/src/flash/core/utilities/flash_cli.py
--- a/src/flash/core/utilities/flash_cli.py
+++ b/src/flash/core/utilities/flash_cli.py
@@ -20,8 +20,7 @@
 from typing import Any, Callable, Dict, List, Optional, Set, Type, Union
 
 import pytorch_lightning as pl
-from jsonargparse import ArgumentParser
-from jsonargparse.signatures import get_class_signature_functions
+from jsonargparse import ArgumentParser, class_from_function
 from lightning_utilities.core.overrides import is_overridden
 from pytorch_lightning import LightningModule, Trainer
 
@@ -31,7 +30,6 @@
     LightningArgumentParser,
     LightningCLI,
     SaveConfigCallback,
-    class_from_function,
 )
 from flash.core.utilities.stability import beta
 
@@ -107,6 +105,16 @@ def wrapper(*args, **kwargs):
     return wrapper
 
 
+def get_class_signature_functions(classes):
+    signatures = []
+    for num, cls in enumerate(classes):
+        if cls.__new__ is not object.__new__ and not any(cls.__new__ is c.__new__ for c in classes[num + 1 :]):
+            signatures.append((cls, cls.__new__))
+        if not any(cls.__init__ is c.__init__ for c in classes[num + 1 :]):
+            signatures.append((cls, cls.__init__))
+    return signatures
+
+
 def get_overlapping_args(func_a, func_b) -> Set[str]:
     func_a = get_class_signature_functions([func_a])[0][1]
     func_b = get_class_signature_functions([func_b])[0][1]
@@ -214,7 +222,7 @@ def add_arguments_to_parser(self, parser) -> None:
     def add_subcommand_from_function(self, subcommands, function, function_name=None):
         subcommand = ArgumentParser()
         if get_kwarg_name(function) == "data_module_kwargs":
-            datamodule_function = class_from_function(function, return_type=self.local_datamodule_class)
+            datamodule_function = class_from_function(function, self.local_datamodule_class)
             subcommand.add_class_arguments(
                 datamodule_function,
                 fail_untyped=False,
@@ -233,7 +241,7 @@ def add_subcommand_from_function(self, subcommands, function, function_name=None
                 },
             )
         else:
-            datamodule_function = class_from_function(drop_kwargs(function), return_type=self.local_datamodule_class)
+            datamodule_function = class_from_function(drop_kwargs(function), self.local_datamodule_class)
             subcommand.add_class_arguments(datamodule_function, fail_untyped=False)
         subcommand_name = function_name or function.__name__
         subcommands.add_subcommand(subcommand_name, subcommand)
diff --git a/src/flash/core/utilities/lightning_cli.py b/src/flash/core/utilities/lightning_cli.py
--- a/src/flash/core/utilities/lightning_cli.py
+++ b/src/flash/core/utilities/lightning_cli.py
@@ -4,14 +4,11 @@
 import os
 import warnings
 from argparse import Namespace
-from functools import wraps
 from types import MethodType
 from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union, cast
 
 import torch
-from jsonargparse import ActionConfigFile, ArgumentParser, set_config_read_mode
-from jsonargparse.signatures import ClassFromFunctionBase
-from jsonargparse.typehints import ClassType
+from jsonargparse import ActionConfigFile, ArgumentParser, class_from_function, set_config_read_mode
 from pytorch_lightning import LightningDataModule, LightningModule, Trainer
 from pytorch_lightning.callbacks import Callback
 from pytorch_lightning.utilities.cloud_io import get_filesystem
@@ -25,46 +22,16 @@
 LRSchedulerType = Union[Type[torch.optim.lr_scheduler._LRScheduler], Type[torch.optim.lr_scheduler.ReduceLROnPlateau]]
 
 
-def class_from_function(
-    func: Callable[..., ClassType],
-    return_type: Optional[Type[ClassType]] = None,
-) -> Type[ClassType]:
-    """Creates a dynamic class which if instantiated is equivalent to calling func.
-
-    Args:
-        func: A function that returns an instance of a class. It must have a return type annotation.
-    """
-
-    @wraps(func)
-    def __new__(cls, *args, **kwargs):
-        return func(*args, **kwargs)
-
-    if return_type is None:
-        return_type = inspect.signature(func).return_annotation
-
-    if isinstance(return_type, str):
-        raise RuntimeError("Classmethod instantiation is not supported when the return type annotation is a string.")
-
-    class ClassFromFunction(return_type, ClassFromFunctionBase):  # type: ignore
-        pass
-
-    ClassFromFunction.__new__ = __new__  # type: ignore
-    ClassFromFunction.__doc__ = func.__doc__
-    ClassFromFunction.__name__ = func.__name__
-
-    return ClassFromFunction
-
-
 class LightningArgumentParser(ArgumentParser):
     """Extension of jsonargparse's ArgumentParser for pytorch-lightning."""
 
-    def __init__(self, *args: Any, parse_as_dict: bool = True, **kwargs: Any) -> None:
+    def __init__(self, *args: Any, **kwargs: Any) -> None:
         """Initialize argument parser that supports configuration file input.
 
         For full details of accepted arguments see
         `ArgumentParser.__init__ <https://jsonargparse.readthedocs.io/en/stable/#jsonargparse.core.ArgumentParser.__init__>`_.
         """
-        super().__init__(*args, parse_as_dict=parse_as_dict, **kwargs)
+        super().__init__(*args, **kwargs)
         self.add_argument(
             "--config", action=ActionConfigFile, help="Path to a configuration file in json or yaml format."
         )
@@ -95,7 +62,7 @@ def add_lightning_class_args(
 
         if inspect.isclass(lightning_class) and issubclass(
             cast(type, lightning_class),
-            (Trainer, LightningModule, LightningDataModule, Callback, ClassFromFunctionBase),
+            (Trainer, LightningModule, LightningDataModule, Callback),
         ):
             if issubclass(cast(type, lightning_class), Callback):
                 self.callback_keys.append(nested_key)


Test patch:
diff --git a/tests/core/utilities/test_lightning_cli.py b/tests/core/utilities/test_lightning_cli.py
--- a/tests/core/utilities/test_lightning_cli.py
+++ b/tests/core/utilities/test_lightning_cli.py
@@ -40,7 +40,7 @@ def test_default_args(mock_argparse, tmpdir):
     """Tests default argument parser for Trainer."""
     mock_argparse.return_value = Namespace(**Trainer.default_attributes())
 
-    parser = LightningArgumentParser(add_help=False, parse_as_dict=False)
+    parser = LightningArgumentParser(add_help=False)
     args = parser.parse_args([])
 
     args.max_epochs = 5
@@ -54,7 +54,7 @@ def test_default_args(mock_argparse, tmpdir):
 @pytest.mark.parametrize("cli_args", [["--accumulate_grad_batches=22"], ["--default_root_dir=./"], []])
 def test_add_argparse_args_redefined(cli_args):
     """Redefines some default Trainer arguments via the cli and tests the Trainer initialization correctness."""
-    parser = LightningArgumentParser(add_help=False, parse_as_dict=False)
+    parser = LightningArgumentParser(add_help=False)
     parser.add_lightning_class_args(Trainer, None)
 
     args = parser.parse_args(cli_args)
@@ -79,11 +79,11 @@ def test_add_argparse_args_redefined(cli_args):
         ("--auto_lr_find=True --auto_scale_batch_size=power", {"auto_lr_find": True, "auto_scale_batch_size": "power"}),
         (
             "--auto_lr_find any_string --auto_scale_batch_size ON",
-            {"auto_lr_find": "any_string", "auto_scale_batch_size": True},
+            {"auto_lr_find": "any_string", "auto_scale_batch_size": "ON"},
         ),
-        ("--auto_lr_find=Yes --auto_scale_batch_size=On", {"auto_lr_find": True, "auto_scale_batch_size": True}),
-        ("--auto_lr_find Off --auto_scale_batch_size No", {"auto_lr_find": False, "auto_scale_batch_size": False}),
-        ("--auto_lr_find TRUE --auto_scale_batch_size FALSE", {"auto_lr_find": True, "auto_scale_batch_size": False}),
+        ("--auto_lr_find=Yes --auto_scale_batch_size=On", {"auto_lr_find": True, "auto_scale_batch_size": "On"}),
+        ("--auto_lr_find Off --auto_scale_batch_size No", {"auto_lr_find": False, "auto_scale_batch_size": "No"}),
+        ("--auto_lr_find TRUE --auto_scale_batch_size FALSE", {"auto_lr_find": True, "auto_scale_batch_size": "FALSE"}),
         ("--limit_train_batches=100", {"limit_train_batches": 100}),
         ("--limit_train_batches 0.8", {"limit_train_batches": 0.8}),
     ],
@@ -91,7 +91,7 @@ def test_add_argparse_args_redefined(cli_args):
 def test_parse_args_parsing(cli_args, expected):
     """Test parsing simple types and None optionals not modified."""
     cli_args = cli_args.split(" ") if cli_args else []
-    parser = LightningArgumentParser(add_help=False, parse_as_dict=False)
+    parser = LightningArgumentParser(add_help=False)
     parser.add_lightning_class_args(Trainer, None)
     with patch("sys.argv", ["any.py"] + cli_args):
         args = parser.parse_args()
@@ -112,7 +112,7 @@ def test_parse_args_parsing(cli_args, expected):
 )
 def test_parse_args_parsing_complex_types(cli_args, expected, instantiate):
     """Test parsing complex types."""
-    parser = LightningArgumentParser(add_help=False, parse_as_dict=False)
+    parser = LightningArgumentParser(add_help=False)
     parser.add_lightning_class_args(Trainer, None)
     with patch("sys.argv", ["any.py"] + cli_args):
         args = parser.parse_args()
@@ -137,7 +137,7 @@ def test_parse_args_parsing_gpus(mocker, cli_args, expected_gpu):
     """Test parsing of gpus and instantiation of Trainer."""
     mocker.patch("lightning_lite.utilities.device_parser._get_all_available_gpus", return_value=[0, 1])
     cli_args = cli_args.split(" ") if cli_args else []
-    parser = LightningArgumentParser(add_help=False, parse_as_dict=False)
+    parser = LightningArgumentParser(add_help=False)
     parser.add_lightning_class_args(Trainer, None)
     with patch("sys.argv", ["any.py"] + cli_args):
         args = parser.parse_args()
@@ -310,8 +310,8 @@ def test_lightning_cli_args(tmpdir):
         config = yaml.safe_load(f.read())
     assert "model" not in config
     assert "model" not in cli.config
-    assert config["data"] == cli.config["data"]
-    assert config["trainer"] == cli.config["trainer"]
+    assert config["data"] == cli.config["data"].as_dict()
+    assert config["trainer"] == cli.config["trainer"].as_dict()
 
 
 @pytest.mark.skipif(not _TOPIC_CORE_AVAILABLE, reason="Not testing core.")
@@ -363,9 +363,9 @@ def test_lightning_cli_config_and_subclass_mode(tmpdir):
     assert os.path.isfile(config_path)
     with open(config_path) as f:
         config = yaml.safe_load(f.read())
-    assert config["model"] == cli.config["model"]
-    assert config["data"] == cli.config["data"]
-    assert config["trainer"] == cli.config["trainer"]
+    assert config["model"] == cli.config["model"].as_dict()
+    assert config["data"] == cli.config["data"].as_dict()
+    assert config["trainer"] == cli.config["trainer"].as_dict()
 
 
 def any_model_any_data_cli():