Compare commits

...

2 Commits

Author SHA1 Message Date
Brad Warren
c2d699711d check oldest value 2023-10-30 12:13:58 -07:00
Brad Warren
72cbb7e072 handle mutable values 2023-10-30 12:05:19 -07:00
2 changed files with 35 additions and 7 deletions

View File

@@ -165,6 +165,7 @@ class HelpfulArgumentParser:
def remove_config_file_domains_for_renewal(self, config: NamespaceConfig) -> None:
"""Make "certbot renew" safe if domains are set in cli.ini."""
# Works around https://github.com/certbot/certbot/issues/4096
assert config.argument_sources is not None
if (config.argument_sources['domains'] == ArgumentSource.CONFIG_FILE and
self.verb == "renew"):
config.domains = []

View File

@@ -66,7 +66,8 @@ class NamespaceConfig:
self.namespace: argparse.Namespace
# Avoid recursion loop because of the delegation defined in __setattr__
object.__setattr__(self, 'namespace', namespace)
object.__setattr__(self, 'argument_sources', None)
object.__setattr__(self, '_argument_sources', None)
object.__setattr__(self, '_previous_mutable_values', {})
self.namespace.config_dir = os.path.abspath(self.namespace.config_dir)
self.namespace.work_dir = os.path.abspath(self.namespace.work_dir)
@@ -90,7 +91,7 @@ class NamespaceConfig:
"""
# Avoid recursion loop because of the delegation defined in __setattr__
object.__setattr__(self, 'argument_sources', argument_sources)
object.__setattr__(self, '_argument_sources', argument_sources)
def set_by_user(self, var: str) -> bool:
@@ -147,13 +148,26 @@ class NamespaceConfig:
be ArgumentSource.RUNTIME. Used when certbot sets an argument's values
at runtime.
"""
if self.argument_sources is not None:
self.argument_sources[name] = ArgumentSource.RUNTIME
if self._argument_sources is not None:
self._argument_sources[name] = ArgumentSource.RUNTIME
@property
def argument_sources(self) -> Optional[Dict[str, ArgumentSource]]:
"""Returns _argument_sources after handling any changes to accessed mutable values."""
for name, prev_value in self._previous_mutable_values.items():
current_value = getattr(self.namespace, name)
if current_value != prev_value:
self._mark_runtime_override(name)
self._previous_mutable_values.clear()
return self._argument_sources
# Delegate any attribute not explicitly defined to the underlying namespace object.
def __getattr__(self, name: str) -> Any:
return getattr(self.namespace, name)
value = getattr(self.namespace, name)
if name not in self._previous_mutable_values and not _is_immutable(value):
self._previous_mutable_values[name] = copy.deepcopy(value)
return value
def __setattr__(self, name: str, value: Any) -> None:
self._mark_runtime_override(name)
@@ -425,8 +439,9 @@ class NamespaceConfig:
# Work around https://bugs.python.org/issue1515 for py26 tests :( :(
new_ns = copy.deepcopy(self.namespace)
new_config = type(self)(new_ns)
if self.set_argument_sources is not None:
new_sources = copy.deepcopy(self.argument_sources)
argument_sources = self.argument_sources
if argument_sources is not None:
new_sources = copy.deepcopy(argument_sources)
new_config.set_argument_sources(new_sources)
return new_config
@@ -450,3 +465,15 @@ def _check_config_sanity(config: NamespaceConfig) -> None:
for domain in config.namespace.domains:
# This may be redundant, but let's be paranoid
util.enforce_domain_sanity(domain)
def _is_immutable(value: Any) -> bool:
"""Is value of an immutable type?"""
if isinstance(value, tuple):
# tuples are only immutable if all contained values are immutable
return all(_is_immutable(subvalue) for subvalue in value)
for immutable_type in (int, float, complex, str, bytes, bool, frozenset,):
if isinstance(value, immutable_type):
return True
# the last case we consider here is None which is also immutable
return value is None