Repository: omni-us/jsonargparse
Pull request #408 (2023-10-23 05:38:25)

Fix AST resolver not using the correct component globals in some cases
## Before submitting

- [x] Did you read the [contributing guideline](https://github.com/omni-us/jsonargparse/blob/main/CONTRIBUTING.rst)?
- [n/a] Did you update **the documentation**? (readme and public docstrings)
- [x] Did you write **unit tests** such that there is 100% coverage on related code? (required for bug fixes and new features)
- [x] Did you verify that new and existing **tests pass locally**?
- [x] Did you make sure that all changes preserve **backward compatibility**?
- [x] Did you update **the CHANGELOG**? (not for typos, docs, test updates, or minor internal changes/refactors)


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408?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:
> Comparison is base [(`373cbb6`)](https://app.codecov.io/gh/omni-us/jsonargparse/commit/373cbb6385940f97409258e0c99a98a33ad4fc21?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) 100.00% compared to head [(`819e236`)](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) 99.79%.

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


```diff
@@             Coverage Diff             @@
##              main     #408      +/-   ##
===========================================
- Coverage   100.00%   99.79%   -0.21%     
===========================================
  Files           21       21              
  Lines         5884     5887       +3     
===========================================
- Hits          5884     5875       -9     
- Misses           0       12      +12     
```

| [Flag](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/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/408/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `88.17% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.10_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `?` | |
| [py3.10_pydantic1](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `?` | |
| [py3.10_pydantic2](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `?` | |
| [py3.10_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `?` | |
| [py3.11](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `?` | |
| [py3.11_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `?` | |
| [py3.11_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `?` | |
| [py3.7](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `88.75% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.7_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.48% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.7_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.46% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.8](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `88.90% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.8_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.50% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.8_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.49% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.9](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `88.75% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.9_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.37% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.9_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.37% <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.

| [Files](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | Coverage Δ | |
|---|---|---|
| [jsonargparse/\_parameter\_resolvers.py](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us#diff-anNvbmFyZ3BhcnNlL19wYXJhbWV0ZXJfcmVzb2x2ZXJzLnB5) | `98.80% <100.00%> (-1.20%)` | :arrow_down: |

... and [2 files with indirect coverage changes](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us)


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/omni-us/jsonargparse/pull/408?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).

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=omni-us_jsonargparse&pullRequest=408)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=408&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=408&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=408&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=408&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=408&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=408&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=omni-us_jsonargparse&pullRequest=408&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=omni-us_jsonargparse&pullRequest=408&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=omni-us_jsonargparse&pullRequest=408&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=408&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=408&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=408&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=omni-us_jsonargparse&pullRequest=408) No Coverage information  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=omni-us_jsonargparse&pullRequest=408&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=omni-us_jsonargparse&pullRequest=408&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index ff55af5..e5b95d1 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -18,6 +18,7 @@ v4.26.1 (2023-10-??)
 Fixed
 ^^^^^
 - Failures when choices is a ``dict_keys`` object and value non-hashable.
+- AST resolver not using the correct component globals in some cases.
 
 
 v4.26.0 (2023-10-19)
diff --git a/jsonargparse/_parameter_resolvers.py b/jsonargparse/_parameter_resolvers.py
index 0addc68..a18a860 100644
--- a/jsonargparse/_parameter_resolvers.py
+++ b/jsonargparse/_parameter_resolvers.py
@@ -9,6 +9,7 @@ from contextlib import contextmanager, suppress
 from contextvars import ContextVar
 from copy import deepcopy
 from functools import partial
+from importlib import import_module
 from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union
 
 from ._common import get_generic_origin, is_dataclass_like, is_generic_class, is_subclass
@@ -541,7 +542,7 @@ class ParametersVisitor(LoggerProperty, ast.NodeVisitor):
     def visit_If(self, node):
         is_test_not = ast_is_not(node.test)
         test_node = node.test.operand if is_test_not else node.test
-        component_globals = self.component.__globals__
+        component_globals = self.get_component_globals()
         if isinstance(test_node, ast.Name) and test_node.id in component_globals:
             condition = bool(component_globals[test_node.id])
             if is_test_not:
@@ -577,6 +578,9 @@ class ParametersVisitor(LoggerProperty, ast.NodeVisitor):
         self.visit(self.component_node)
         return self.values_found
 
+    def get_component_globals(self):
+        return vars(import_module(self.component.__module__))
+
     def get_component_from_source(self, name, source):
         aliases = {}
         ast_exec = ast.parse("")
@@ -672,7 +676,7 @@ class ParametersVisitor(LoggerProperty, ast.NodeVisitor):
 
     def get_call_class_type(self, node) -> Optional[type]:
         names = ast_get_name_and_attrs(getattr(node, "func", None))
-        class_type = self.component.__globals__.get(names[0]) if names else None
+        class_type = self.get_component_globals().get(names[0]) if names else None
         for name in names[1:]:
             class_type = getattr(class_type, name, None)
         return class_type if inspect.isclass(class_type) else None


Test patch:
diff --git a/jsonargparse_tests/conftest.py b/jsonargparse_tests/conftest.py
index eaac18a..6dc8238 100644
--- a/jsonargparse_tests/conftest.py
+++ b/jsonargparse_tests/conftest.py
@@ -2,6 +2,7 @@ import logging
 import os
 import platform
 from contextlib import ExitStack, contextmanager, redirect_stderr, redirect_stdout
+from functools import wraps
 from importlib.util import find_spec
 from io import StringIO
 from pathlib import Path
@@ -157,3 +158,16 @@ def get_parse_args_stderr(parser: ArgumentParser, args: List[str]) -> str:
         with redirect_stderr(err), pytest.raises(SystemExit):
             parser.parse_args(args)
     return err.getvalue()
+
+
+class BaseClass:
+    def __init__(self):
+        pass
+
+
+def wrap_fn(fn):
+    @wraps(fn)
+    def wrapped_fn(*args, **kwargs):
+        return fn(*args, **kwargs)
+
+    return wrapped_fn
diff --git a/jsonargparse_tests/test_parameter_resolvers.py b/jsonargparse_tests/test_parameter_resolvers.py
index 483749d..66257fd 100644
--- a/jsonargparse_tests/test_parameter_resolvers.py
+++ b/jsonargparse_tests/test_parameter_resolvers.py
@@ -14,7 +14,7 @@ from jsonargparse import Namespace, class_from_function
 from jsonargparse._optionals import docstring_parser_support
 from jsonargparse._parameter_resolvers import ConditionalDefault, is_lambda
 from jsonargparse._parameter_resolvers import get_signature_parameters as get_params
-from jsonargparse_tests.conftest import capture_logs, source_unavailable
+from jsonargparse_tests.conftest import BaseClass, capture_logs, source_unavailable, wrap_fn
 
 
 class ClassA:
@@ -391,6 +391,20 @@ class ClassInstanceDefaults:
         """
 
 
+GLOBAL_CONSTANT = False
+
+
+class ConditionalGlobalConstant(BaseClass):
+    @wrap_fn
+    def __init__(self, **kwargs):
+        super().__init__()
+        kwargs.get("p1", "x")
+        if GLOBAL_CONSTANT:
+            kwargs.get("p2", "y")
+        else:
+            kwargs.get("p3", "z")
+
+
 def function_no_args_no_kwargs(pk1: str, k2: int = 1):
     """
     Args:
@@ -680,6 +694,12 @@ def test_get_params_class_instance_defaults(subtests):
         assert is_lambda(params[8].default)
 
 
+def test_get_params_class_conditional_global_constant():
+    params = get_params(ConditionalGlobalConstant)
+    assert ["p1", "p3"] == [p.name for p in params]
+    assert "GLOBAL_CONSTANT" not in ConditionalGlobalConstant.__init__.__globals__
+
+
 # class method parameters tests