Skip to content

Commit a0219db

Browse files
committed
Fixed optional packages being imported even though not used.
1 parent 8be44b3 commit a0219db

File tree

7 files changed

+37
-30
lines changed

7 files changed

+37
-30
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Fixed
2121
^^^^^
2222
- Failure to parse when subcommand has no options `#158
2323
<https://github.com/omni-us/jsonargparse/issues/158>`__.
24+
- Optional packages being imported even though not used.
2425

2526

2627
v4.13.2 (2022-08-31)

jsonargparse/core.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@
99
import sys
1010
from copy import deepcopy
1111
from typing import Any, Callable, Dict, List, NoReturn, Optional, Sequence, Set, Tuple, Type, Union
12-
from unittest.mock import patch
1312

1413
from .formatters import DefaultHelpFormatter, empty_help, formatter_context, get_env_var
1514
from .jsonnet import ActionJsonnet
1615
from .jsonschema import ActionJsonSchema
1716
from .link_arguments import ActionLink, ArgumentLinking
1817
from .loaders_dumpers import check_valid_dump_format, dump_using_format, get_loader_exceptions, loaders, load_value, load_value_context, yaml_load
19-
from .namespace import dict_to_namespace, is_meta_key, Namespace, split_key, split_key_leaf, strip_meta
18+
from .namespace import dict_to_namespace, is_meta_key, Namespace, patch_namespace, split_key, split_key_leaf, strip_meta
2019
from .signatures import is_pure_dataclass, SignatureArguments
2120
from .typehints import ActionTypeHint, is_subclass_spec
2221
from .typing import is_final_class
@@ -233,7 +232,7 @@ def parse_known_args(self, args=None, namespace=None):
233232
namespace = argcomplete_namespace(caller, self, namespace)
234233

235234
try:
236-
with patch('argparse.Namespace', Namespace), lenient_check_context(caller), ActionTypeHint.subclass_arg_context(self), load_value_context(self.parser_mode):
235+
with patch_namespace(), lenient_check_context(caller), ActionTypeHint.subclass_arg_context(self), load_value_context(self.parser_mode):
237236
namespace, args = self._parse_known_args(args, namespace)
238237
except (argparse.ArgumentError, ParserError) as ex:
239238
self.error(str(ex), ex)
@@ -1409,6 +1408,10 @@ def parser_mode(self) -> str:
14091408

14101409
@parser_mode.setter
14111410
def parser_mode(self, parser_mode: str):
1411+
if parser_mode not in loaders and parser_mode == 'omegaconf' and omegaconf_support:
1412+
from .loaders_dumpers import set_loader
1413+
from .optionals import get_omegaconf_loader
1414+
set_loader('omegaconf', get_omegaconf_loader())
14121415
if parser_mode not in loaders:
14131416
raise ValueError(f'The only accepted values for parser_mode are {set(loaders.keys())}.')
14141417
if parser_mode == 'jsonnet':
@@ -1439,12 +1442,6 @@ def dump_header(self, dump_header: Optional[List[str]]):
14391442
self._dump_header = dump_header
14401443

14411444

1442-
if omegaconf_support:
1443-
from .loaders_dumpers import set_loader
1444-
from .optionals import get_omegaconf_loader
1445-
set_loader('omegaconf', get_omegaconf_loader())
1446-
1447-
14481445
from .deprecated import parse_as_dict_patch, instantiate_subclasses_patch
14491446
instantiate_subclasses_patch()
14501447
if 'JSONARGPARSE_SKIP_DEPRECATION_PATCH' not in os.environ:

jsonargparse/namespace.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Classes and functions related to namespace objects."""
22

3-
from argparse import Namespace as ArgparseNamespace
3+
import argparse
4+
from contextlib import contextmanager
45
from copy import deepcopy
56
from typing import Any, Callable, Dict, Iterator, List, Optional, overload, Tuple, Union
67

@@ -67,6 +68,16 @@ def strip_meta(cfg):
6768
return cfg
6869

6970

71+
@contextmanager
72+
def patch_namespace():
73+
namespace_class = argparse.Namespace
74+
argparse.Namespace = Namespace
75+
try:
76+
yield
77+
finally:
78+
argparse.Namespace = namespace_class
79+
80+
7081
def namespace_to_dict(namespace: 'Namespace') -> Dict[str, Any]:
7182
"""Returns a deepcopy of a nested namespace converted into a nested dictionary."""
7283
return namespace.clone().as_dict()
@@ -87,14 +98,14 @@ def expand_dict(cfg):
8798
return expand_dict(cfg_dict)
8899

89100

90-
class Namespace(ArgparseNamespace):
101+
class Namespace(argparse.Namespace):
91102
"""Extension of argparse's Namespace to support nesting and subscript access."""
92103

93104
def __init__(self, *args, **kwargs):
94105
if len(args) == 0:
95106
super().__init__(**kwargs)
96107
else:
97-
if len(kwargs) != 0 or len(args) != 1 or not isinstance(args[0], (ArgparseNamespace, dict)):
108+
if len(kwargs) != 0 or len(args) != 1 or not isinstance(args[0], (argparse.Namespace, dict)):
98109
raise ValueError('Expected a single positional parameter of type Namespace or dict.')
99110
for key, val in (args[0].items() if type(args[0]) is dict else vars(args[0]).items()):
100111
self[key] = val
@@ -211,9 +222,9 @@ def as_dict(self) -> Dict[str, Any]:
211222
dic[key] = val
212223
return dic
213224

214-
def as_flat(self) -> ArgparseNamespace:
225+
def as_flat(self) -> argparse.Namespace:
215226
"""Converts the nested namespaces into a single argparse flat namespace."""
216-
flat = ArgparseNamespace()
227+
flat = argparse.Namespace()
217228
for key, val in self.items():
218229
setattr(flat, key, val)
219230
return flat

jsonargparse/optionals.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,9 @@ class UndefinedException(Exception):
5050
pass
5151

5252

53-
if jsonschema_support:
54-
from jsonschema.exceptions import ValidationError as jsonschemaValidationError
55-
else:
56-
jsonschemaValidationError = UndefinedException
57-
58-
5953
def get_jsonschema_exceptions():
60-
return (jsonschemaValidationError,)
54+
from jsonschema.exceptions import ValidationError
55+
return (ValidationError,)
6156

6257

6358
@contextmanager
@@ -173,15 +168,15 @@ def parse_docs(component, parent, logger):
173168
return docs
174169

175170

176-
files_completer = None
177-
if argcomplete_support:
171+
def get_files_completer():
178172
from argcomplete.completers import FilesCompleter
179-
files_completer = FilesCompleter()
173+
return FilesCompleter()
180174

181175

182176
class FilesCompleterMethod:
183177
"""Completer method for Action classes that should complete files."""
184178
def completer(self, prefix, **kwargs):
179+
files_completer = get_files_completer()
185180
return sorted(files_completer(prefix, **kwargs))
186181

187182

jsonargparse/typehints.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@
3333

3434
from .actions import _ActionHelpClassPath, _find_action, _find_parent_action, _is_action_value_list
3535
from .loaders_dumpers import get_loader_exceptions, load_value
36-
from .namespace import Namespace, split_key_root
36+
from .namespace import Namespace
3737
from .typing import is_final_class, registered_types
3838
from .optionals import (
3939
argcomplete_warn_redraw_prompt,
40-
files_completer,
40+
get_files_completer,
4141
typing_extensions_import,
4242
)
4343
from .util import (
@@ -432,6 +432,7 @@ def completer(self, prefix, **kwargs):
432432
enum = self._typehint.__args__[0]
433433
return list(enum.__members__.keys())+['null']
434434
elif is_optional(self._typehint, Path):
435+
files_completer = get_files_completer()
435436
return ['null'] + sorted(files_completer(prefix, **kwargs))
436437
elif chr(int(os.environ['COMP_TYPE'])) == '?':
437438
try:

jsonargparse_tests/test_core.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import pickle
66
import sys
77
import unittest
8+
import unittest.mock
89
import warnings
910
import yaml
1011
from io import StringIO

jsonargparse_tests/test_namespace.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#!/usr/bin/env python3
22

3+
import argparse
34
import unittest
4-
from jsonargparse.namespace import ArgparseNamespace, Namespace, namespace_to_dict, dict_to_namespace
5+
from jsonargparse.namespace import Namespace, namespace_to_dict, dict_to_namespace
56
from jsonargparse_tests.base import is_cpython
67

78

@@ -148,7 +149,7 @@ def test_as_flat(self):
148149
ns['w'] = 1
149150
ns['x.y.z'] = 2
150151
flat = ns.as_flat()
151-
self.assertIsInstance(flat, ArgparseNamespace)
152+
self.assertIsInstance(flat, argparse.Namespace)
152153
self.assertEqual(vars(flat), {'w': 1, 'x.y.z': 2})
153154

154155
def test_clone(self):
@@ -172,7 +173,7 @@ def test_update_invalid(self):
172173
self.assertRaises(KeyError, lambda: ns.update(123))
173174

174175
def test_init_from_argparse_flat_namespace(self):
175-
argparse_ns = ArgparseNamespace()
176+
argparse_ns = argparse.Namespace()
176177
setattr(argparse_ns, 'w', 0)
177178
setattr(argparse_ns, 'x.y.a', 1)
178179
setattr(argparse_ns, 'x.y.b', 2)
@@ -182,7 +183,7 @@ def test_init_from_argparse_flat_namespace(self):
182183

183184
def test_init_invalid(self):
184185
self.assertRaises(ValueError, lambda: Namespace(1))
185-
self.assertRaises(ValueError, lambda: Namespace(ArgparseNamespace(), x=1))
186+
self.assertRaises(ValueError, lambda: Namespace(argparse.Namespace(), x=1))
186187

187188
def test_namespace_to_dict(self):
188189
ns = Namespace()

0 commit comments

Comments
 (0)