Skip to content

Commit 8f2dcbf

Browse files
SNOW-878611 renaming and aliasing CONFIG_PARSER to CONFIG_MANAGER (#1680)
Co-authored-by: Barry Warsaw <[email protected]>
1 parent 58e2036 commit 8f2dcbf

File tree

5 files changed

+167
-60
lines changed

5 files changed

+167
-60
lines changed

DESCRIPTION.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ Source code is also available at: https://github.com/snowflakedb/snowflake-conne
88

99
# Release Notes
1010

11+
- v3.1.2(TBD)
12+
13+
- Made the ``parser`` -> ``manager`` renaming more consistent in ``snowflake.connector.config_manager`` module.
14+
1115
- v3.1.1(August 28,2023)
1216

1317
- Fixed a bug in retry logic for okta authentication to refresh token.
@@ -16,7 +20,6 @@ Source code is also available at: https://github.com/snowflakedb/snowflake-conne
1620
- Cherry-picked https://github.com/urllib3/urllib3/commit/fd2759aa16b12b33298900c77d29b3813c6582de onto vendored urllib3 (v1.26.15) to enable enforce_content_length by default.
1721
- Fixed a bug in tag generation of OOB telemetry event.
1822

19-
2023
- v3.1.0(July 31,2023)
2124

2225
- Added a feature that lets you add connection definitions to the `connections.toml` configuration file. A connection definition refers to a collection of connection parameters, for example, if you wanted to define a connection named `prod``:

src/snowflake/connector/config_manager.py

Lines changed: 112 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import logging
99
import os
1010
import stat
11+
import warnings
1112
from collections.abc import Iterable
1213
from operator import methodcaller
1314
from pathlib import Path
@@ -46,9 +47,11 @@ class ConfigSlice(NamedTuple):
4647
class ConfigOption:
4748
"""ConfigOption represents a flag/setting.
4849
49-
The class knows how to read the value out of all sources and implements
50+
This class knows how to read the value out of all different sources and implements
5051
order of precedence between them.
5152
53+
It also provides value parsing and verification.
54+
5255
Attributes:
5356
name: Name of this ConfigOption.
5457
parse_str: A function that can turn str to the desired type, useful
@@ -57,14 +60,14 @@ class ConfigOption:
5760
this option.
5861
env_name: Environmental variable value should be read from, if not
5962
supplied, we'll construct this. False disables reading from
60-
environmental variable, None uses the auto generated variable name
63+
environmental variables, None uses the auto generated variable name
6164
and explicitly provided string overwrites the default one.
62-
_root_manager: Reference to the root parser. Used to efficiently
65+
_root_manager: Reference to the root manager. Used to efficiently
6366
refer to cached config file. Is supplied by the parent
6467
ConfigManager.
6568
_nest_path: The names of the ConfigManagers that this option is
66-
nested in. Used be able to efficiently resolve where to grab
67-
value out of configuration file and construct environment
69+
nested in. Used to be able to efficiently resolve where to retrieve
70+
value out of the configuration file and construct environment
6871
variable name. This is supplied by the parent ConfigManager.
6972
"""
7073

@@ -78,14 +81,17 @@ def __init__(
7881
_root_manager: ConfigManager | None = None,
7982
_nest_path: list[str] | None,
8083
) -> None:
81-
"""Create a config option that can read values from different locations.
84+
"""Create a config option that can read values from different sources.
8285
8386
Args:
8487
name: Name to assign to this ConfigOption.
85-
parse_str: String parser function for this ConfigOption.
86-
choices: List of possible values for this ConfigOption.
88+
parse_str: String parser function for this instance.
89+
choices: List of possible values for this instance.
8790
env_name: Environmental variable name value should be read from.
88-
_root_manager: Reference to the root parser. Should be supplied by
91+
Providing a string will use that environment variable, False disables
92+
reading value from environmental variables and the default None generates
93+
an environmental variable name for it using the _nest_path and name.
94+
_root_manager: Reference to the root manager. Should be supplied by
8995
the parent ConfigManager.
9096
_nest_path: The names of the ConfigManagers that this option is
9197
nested in. This is supplied by the parent ConfigManager.
@@ -155,7 +161,11 @@ def _get_env(self) -> tuple[bool, str | _T | None]:
155161
return True, loaded_var
156162

157163
def _get_config(self) -> Any:
158-
"""Get value from the cached config file."""
164+
"""Get value from the cached config file if possible.
165+
166+
Since this is the last resource for retrieving the value it raises
167+
a MissingConfigOptionError if it's unable to find this option.
168+
"""
159169
if (
160170
self._root_manager.conf_file_cache is None
161171
and self._root_manager.file_path is not None
@@ -164,7 +174,7 @@ def _get_config(self) -> Any:
164174
e = self._root_manager.conf_file_cache
165175
if e is None:
166176
raise ConfigManagerError(
167-
f"Root parser '{self._root_manager.name}' is missing file_path",
177+
f"Root manager '{self._root_manager.name}' is missing file_path",
168178
)
169179
for k in self._nest_path[1:]:
170180
try:
@@ -183,13 +193,21 @@ def _get_config(self) -> Any:
183193

184194

185195
class ConfigManager:
186-
"""Read TOML configuration file with managed multi-source precedence.
196+
"""Read a TOML configuration file with managed multi-source precedence.
197+
198+
Note that multi-source precedence is actually implemented by ConfigOption.
199+
This is done to make sure that special handling can be done for special options.
200+
As an example, think of not allowing to provide passwords by command line arguments.
201+
202+
This class is updatable at run-time, allowing other libraries to add their
203+
own configuration options and sub-managers before resolution.
187204
188-
This class is updatable at run-time, allowing other libraries to add their
189-
own configuration options and sub-parsers. Sub-parsers allow
190-
options groups to exist, e.g. the group "snowflake.cli.output" could have
191-
2 options in it: debug (boolean flag) and format (a string like "json", or
192-
"csv").
205+
This class can simply be thought of as nestable containers for ConfigOptions.
206+
It holds extra information necessary for efficient nesting purposes.
207+
208+
Sub-managers allow option groups to exist, e.g. the group "snowflake.cli.output"
209+
could have 2 options in it: debug (boolean flag) and format (a string like "json",
210+
or "csv").
193211
194212
When a ConfigManager tries to retrieve ConfigOptions' value the _root_manager
195213
will read and cache the TOML file from the file it's pointing at, afterwards
@@ -200,14 +218,20 @@ class ConfigManager:
200218
useful error messages.
201219
file_path: Path to the file where this and all child ConfigManagers
202220
should read their values out of. Can be omitted for all child
203-
parsers.
221+
managers. Root manager could also miss this value, but this will
222+
result in an exception when a value is read that isn't available from
223+
a preceding config source.
204224
conf_file_cache: Cache to store what we read from the TOML file.
205-
_sub_parsers: List of ConfigManagers that are nested under us.
206-
_options: List of ConfigOptions that are our children.
207-
_root_manager: Reference to root parser. Used to efficiently propagate to
225+
_sub_managers: List of ConfigManagers that are nested under the current manager.
226+
_sub_parsers: Alias for the old name of _sub_managers in the first release, please use
227+
the new name now, as this might get deprecated in the future.
228+
_options: List of ConfigOptions that are under the current manager.
229+
_root_manager: Reference to the root manager. Used to efficiently propagate to
208230
child options.
209-
_nest_path: The names of the ConfigManagers that this parser is nested
231+
_nest_path: The names of the ConfigManagers that this manager is nested
210232
under. Used to efficiently propagate to child options.
233+
_slices: List of config slices, where optional sections could be read from.
234+
Note that this feature might become deprecated soon.
211235
"""
212236

213237
def __init__(
@@ -217,37 +241,54 @@ def __init__(
217241
file_path: Path | None = None,
218242
_slices: list[ConfigSlice] | None = None,
219243
):
220-
"""Create a new ConfigManager.
244+
"""Creates a new ConfigManager.
221245
222246
Args:
223247
name: Name of this ConfigManager.
224-
file_path: File this parser should read values from. Can be omitted
225-
for all child parsers.
248+
file_path: File this manager should read values from. Can be omitted
249+
for all child managers.
250+
_slices: List of ConfigSlices to consider. A configuration file's slice is a
251+
section that can optionally reside in a different file. Note that this
252+
feature might get deprecated soon.
226253
"""
227254
if _slices is None:
228255
_slices = list()
229256
self.name = name
230257
self.file_path = file_path
231258
self._slices = _slices
232-
# Objects holding subparsers and options
259+
# Objects holding sub-managers and options
233260
self._options: dict[str, ConfigOption] = dict()
234-
self._sub_parsers: dict[str, ConfigManager] = dict()
261+
self._sub_managers: dict[str, ConfigManager] = dict()
235262
# Dictionary to cache read in config file
236263
self.conf_file_cache: tomlkit.TOMLDocument | None = None
237264
# Information necessary to be able to nest elements
238265
# and add options in O(1)
239266
self._root_manager: ConfigManager = self
240267
self._nest_path = [name]
241268

269+
@property
270+
def _sub_parsers(self) -> dict[str, ConfigManager]:
271+
"""
272+
Alias for the old name of ``_sub_managers``.
273+
274+
This used to be the original name in the first release, please use the
275+
new name, as this might get deprecated in the future.
276+
"""
277+
warnings.warn(
278+
"_sub_parsers has been deprecated, use _sub_managers instead",
279+
DeprecationWarning,
280+
stacklevel=2,
281+
)
282+
return self._sub_managers
283+
242284
def read_config(
243285
self,
244286
) -> None:
245-
"""Read and cache config file.
287+
"""Read and cache config file contents.
246288
247-
This function should be called if the ConfigManager's cache is outdated.
248-
Maybe in the case when we want to replace the file_path assigned to a
249-
ConfigManager, or if one's doing development and are interactively
250-
adding new options to their configuration files.
289+
This function should be explicitly called if the ConfigManager's cache is
290+
outdated. Most likely when someone's doing development and are interactively
291+
adding new options to their configuration file.
251292
"""
252293
if self.file_path is None:
253294
raise ConfigManagerError(
@@ -297,10 +338,10 @@ def add_option(
297338
option_cls: type[ConfigOption] = ConfigOption,
298339
**kwargs,
299340
) -> None:
300-
"""Add an ConfigOption to this ConfigManager.
341+
"""Add a ConfigOption to this ConfigManager.
301342
302343
Args:
303-
option_cls: The class that should be instantiated. This is class
344+
option_cls: The class that should be instantiated. This class
304345
should be a child class of ConfigOption. Mainly useful for cases
305346
where the default ConfigOption needs to be extended, for example
306347
if a new configuration option source needs to be supported.
@@ -314,17 +355,17 @@ def add_option(
314355
self._options[new_option.name] = new_option
315356

316357
def _check_child_conflict(self, name: str) -> None:
317-
"""Check if a sub-parser, or ConfigOption conflicts with given name.
358+
"""Check if a sub-manager, or ConfigOption conflicts with given name.
318359
319360
Args:
320361
name: Name to check against children.
321362
"""
322-
if name in (self._options.keys() | self._sub_parsers.keys()):
363+
if name in (self._options.keys() | self._sub_managers.keys()):
323364
raise ConfigManagerError(
324-
f"'{name}' subparser, or option conflicts with a child element of '{self.name}'"
365+
f"'{name}' sub-manager, or option conflicts with a child element of '{self.name}'"
325366
)
326367

327-
def add_subparser(self, new_child: ConfigManager) -> None:
368+
def add_submanager(self, new_child: ConfigManager) -> None:
328369
"""Nest another ConfigManager under this one.
329370
330371
This function recursively updates _nest_path and _root_manager of all
@@ -334,44 +375,52 @@ def add_subparser(self, new_child: ConfigManager) -> None:
334375
new_child: The ConfigManager to be nested under the current one.
335376
Notes:
336377
We currently don't support re-nesting a ConfigManager. Only nest a
337-
parser under another one once.
378+
manager under another one once.
338379
"""
339380
self._check_child_conflict(new_child.name)
340-
self._sub_parsers[new_child.name] = new_child
381+
self._sub_managers[new_child.name] = new_child
341382

342383
def _root_setter_helper(node: ConfigManager):
343384
# Deal with ConfigManagers
344385
node._root_manager = self._root_manager
345386
node._nest_path = self._nest_path + node._nest_path
346-
for sub_parser in node._sub_parsers.values():
347-
_root_setter_helper(sub_parser)
387+
for sub_manager in node._sub_managers.values():
388+
_root_setter_helper(sub_manager)
348389
# Deal with ConfigOptions
349390
for option in node._options.values():
350391
option._root_manager = self._root_manager
351392
option._nest_path = self._nest_path + option._nest_path
352393

353394
_root_setter_helper(new_child)
354395

396+
def add_subparser(self, *args, **kwargs) -> None:
397+
warnings.warn(
398+
"add_subparser has been deprecated, use add_submanager instead",
399+
DeprecationWarning,
400+
stacklevel=2,
401+
)
402+
return self.add_submanager(*args, **kwargs)
403+
355404
def __getitem__(self, name: str) -> ConfigOption | ConfigManager:
356-
"""Get either sub-parser, or option in this parser with name.
405+
"""Get either sub-manager, or option in this manager with name.
357406
358-
If option is retrieved, we call get() on it to return its value instead.
407+
If an option is retrieved, we call get() on it to return its value instead.
359408
360409
Args:
361410
name: Name to retrieve.
362411
"""
363412
if name in self._options:
364413
return self._options[name].value()
365-
if name not in self._sub_parsers:
414+
if name not in self._sub_managers:
366415
raise ConfigSourceError(
367416
"No ConfigManager, or ConfigOption can be found"
368417
f" with the name '{name}'"
369418
)
370-
return self._sub_parsers[name]
419+
return self._sub_managers[name]
371420

372421

373-
CONFIG_PARSER = ConfigManager(
374-
name="CONFIG_PARSER",
422+
CONFIG_MANAGER = ConfigManager(
423+
name="CONFIG_MANAGER",
375424
file_path=CONFIG_FILE,
376425
_slices=[
377426
ConfigSlice( # Optional connections file to read in connections from
@@ -383,13 +432,26 @@ def __getitem__(self, name: str) -> ConfigOption | ConfigManager:
383432
),
384433
],
385434
)
386-
CONFIG_PARSER.add_option(
435+
CONFIG_MANAGER.add_option(
387436
name="connections",
388437
parse_str=tomlkit.parse,
389438
)
390439

391-
__all__ = [
440+
441+
def __getattr__(name):
442+
if name == "CONFIG_PARSER":
443+
warnings.warn(
444+
"CONFIG_PARSER has been deprecated, use CONFIG_MANAGER instead",
445+
DeprecationWarning,
446+
stacklevel=2,
447+
)
448+
return CONFIG_MANAGER
449+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
450+
451+
452+
__all__ = [ # noqa: F822
392453
"ConfigOption",
393454
"ConfigManager",
455+
"CONFIG_MANAGER",
394456
"CONFIG_PARSER",
395457
]

src/snowflake/connector/connection.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
from .auth.idtoken import AuthByIdToken
4444
from .bind_upload_agent import BindUploadError
4545
from .compat import IS_LINUX, IS_WINDOWS, quote, urlencode
46-
from .config_manager import CONFIG_PARSER
46+
from .config_manager import CONFIG_MANAGER
4747
from .connection_diagnostic import ConnectionDiagnostic
4848
from .constants import (
4949
ENV_VAR_PARTNER,
@@ -336,13 +336,13 @@ def __init__(
336336
self.query_context_cache_size = 5
337337
if connections_file_path is not None:
338338
# Change config file path and force update cache
339-
for i, s in enumerate(CONFIG_PARSER._slices):
339+
for i, s in enumerate(CONFIG_MANAGER._slices):
340340
if s.section == "connections":
341-
CONFIG_PARSER._slices[i] = s._replace(path=connections_file_path)
342-
CONFIG_PARSER.read_config()
341+
CONFIG_MANAGER._slices[i] = s._replace(path=connections_file_path)
342+
CONFIG_MANAGER.read_config()
343343
break
344344
if connection_name is not None:
345-
connections = CONFIG_PARSER["connections"]
345+
connections = CONFIG_MANAGER["connections"]
346346
if connection_name not in connections:
347347
raise Error(
348348
f"Invalid connection_name '{connection_name}',"

src/snowflake/connector/errors.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ class MissingConfigOptionError(ConfigSourceError):
620620

621621

622622
class ConfigManagerError(Error):
623-
"""Configuration parser related errors.
623+
"""Configuration manager related errors.
624624
625-
These mean that ConfigManager is misused by a developer.
625+
This means that ConfigManager is misused by a developer.
626626
"""

0 commit comments

Comments
 (0)