Re-type config paths in ConfigErrors to be StrSequences (#15615)

Part of #14809.

Signed-off-by: Sean Quah <seanq@matrix.org>
This commit is contained in:
Sean Quah 2023-05-18 11:11:30 +01:00 committed by GitHub
parent e15aa00bc0
commit 68dcd2cbcb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 31 additions and 26 deletions

1
changelog.d/15615.misc Normal file
View File

@ -0,0 +1 @@
Re-type config paths in `ConfigError`s to be `StrSequence`s instead of `Iterable[str]`s.

View File

@ -44,6 +44,7 @@ import jinja2
import pkg_resources import pkg_resources
import yaml import yaml
from synapse.types import StrSequence
from synapse.util.templates import _create_mxc_to_http_filter, _format_ts_filter from synapse.util.templates import _create_mxc_to_http_filter, _format_ts_filter
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -58,7 +59,7 @@ class ConfigError(Exception):
the problem lies. the problem lies.
""" """
def __init__(self, msg: str, path: Optional[Iterable[str]] = None): def __init__(self, msg: str, path: Optional[StrSequence] = None):
self.msg = msg self.msg = msg
self.path = path self.path = path

View File

@ -61,9 +61,10 @@ from synapse.config import ( # noqa: F401
voip, voip,
workers, workers,
) )
from synapse.types import StrSequence
class ConfigError(Exception): class ConfigError(Exception):
def __init__(self, msg: str, path: Optional[Iterable[str]] = None): def __init__(self, msg: str, path: Optional[StrSequence] = None):
self.msg = msg self.msg = msg
self.path = path self.path = path

View File

@ -11,17 +11,17 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from typing import Any, Dict, Iterable, Type, TypeVar from typing import Any, Dict, Type, TypeVar
import jsonschema import jsonschema
from pydantic import BaseModel, ValidationError, parse_obj_as from pydantic import BaseModel, ValidationError, parse_obj_as
from synapse.config._base import ConfigError from synapse.config._base import ConfigError
from synapse.types import JsonDict from synapse.types import JsonDict, StrSequence
def validate_config( def validate_config(
json_schema: JsonDict, config: Any, config_path: Iterable[str] json_schema: JsonDict, config: Any, config_path: StrSequence
) -> None: ) -> None:
"""Validates a config setting against a JsonSchema definition """Validates a config setting against a JsonSchema definition
@ -45,7 +45,7 @@ def validate_config(
def json_error_to_config_error( def json_error_to_config_error(
e: jsonschema.ValidationError, config_path: Iterable[str] e: jsonschema.ValidationError, config_path: StrSequence
) -> ConfigError: ) -> ConfigError:
"""Converts a json validation error to a user-readable ConfigError """Converts a json validation error to a user-readable ConfigError

View File

@ -19,7 +19,7 @@ from urllib import parse as urlparse
import attr import attr
import pkg_resources import pkg_resources
from synapse.types import JsonDict from synapse.types import JsonDict, StrSequence
from ._base import Config, ConfigError from ._base import Config, ConfigError
from ._util import validate_config from ._util import validate_config
@ -80,7 +80,7 @@ class OembedConfig(Config):
) )
def _parse_and_validate_provider( def _parse_and_validate_provider(
self, providers: List[JsonDict], config_path: Iterable[str] self, providers: List[JsonDict], config_path: StrSequence
) -> Iterable[OEmbedEndpointConfig]: ) -> Iterable[OEmbedEndpointConfig]:
# Ensure it is the proper form. # Ensure it is the proper form.
validate_config( validate_config(
@ -112,7 +112,7 @@ class OembedConfig(Config):
api_endpoint, patterns, endpoint.get("formats") api_endpoint, patterns, endpoint.get("formats")
) )
def _glob_to_pattern(self, glob: str, config_path: Iterable[str]) -> Pattern: def _glob_to_pattern(self, glob: str, config_path: StrSequence) -> Pattern:
""" """
Convert the glob into a sane regular expression to match against. The Convert the glob into a sane regular expression to match against. The
rules followed will be slightly different for the domain portion vs. rules followed will be slightly different for the domain portion vs.

View File

@ -27,7 +27,7 @@ from netaddr import AddrFormatError, IPNetwork, IPSet
from twisted.conch.ssh.keys import Key from twisted.conch.ssh.keys import Key
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
from synapse.types import JsonDict from synapse.types import JsonDict, StrSequence
from synapse.util.module_loader import load_module from synapse.util.module_loader import load_module
from synapse.util.stringutils import parse_and_validate_server_name from synapse.util.stringutils import parse_and_validate_server_name
@ -73,7 +73,7 @@ def _6to4(network: IPNetwork) -> IPNetwork:
def generate_ip_set( def generate_ip_set(
ip_addresses: Optional[Iterable[str]], ip_addresses: Optional[Iterable[str]],
extra_addresses: Optional[Iterable[str]] = None, extra_addresses: Optional[Iterable[str]] = None,
config_path: Optional[Iterable[str]] = None, config_path: Optional[StrSequence] = None,
) -> IPSet: ) -> IPSet:
""" """
Generate an IPSet from a list of IP addresses or CIDRs. Generate an IPSet from a list of IP addresses or CIDRs.

View File

@ -84,7 +84,15 @@ JsonSerializable = object
# Collection[str] that does not include str itself; str being a Sequence[str] # Collection[str] that does not include str itself; str being a Sequence[str]
# is very misleading and results in bugs. # is very misleading and results in bugs.
#
# StrCollection is an unordered collection of strings. If ordering is important,
# StrSequence can be used instead.
StrCollection = Union[Tuple[str, ...], List[str], AbstractSet[str]] StrCollection = Union[Tuple[str, ...], List[str], AbstractSet[str]]
# Sequence[str] that does not include str itself; str being a Sequence[str]
# is very misleading and results in bugs.
#
# Unlike StrCollection, StrSequence is an ordered collection of strings.
StrSequence = Union[Tuple[str, ...], List[str]]
# Note that this seems to require inheriting *directly* from Interface in order # Note that this seems to require inheriting *directly* from Interface in order

View File

@ -14,17 +14,17 @@
import importlib import importlib
import importlib.util import importlib.util
import itertools
from types import ModuleType from types import ModuleType
from typing import Any, Iterable, Tuple, Type from typing import Any, Tuple, Type
import jsonschema import jsonschema
from synapse.config._base import ConfigError from synapse.config._base import ConfigError
from synapse.config._util import json_error_to_config_error from synapse.config._util import json_error_to_config_error
from synapse.types import StrSequence
def load_module(provider: dict, config_path: Iterable[str]) -> Tuple[Type, Any]: def load_module(provider: dict, config_path: StrSequence) -> Tuple[Type, Any]:
"""Loads a synapse module with its config """Loads a synapse module with its config
Args: Args:
@ -39,9 +39,7 @@ def load_module(provider: dict, config_path: Iterable[str]) -> Tuple[Type, Any]:
modulename = provider.get("module") modulename = provider.get("module")
if not isinstance(modulename, str): if not isinstance(modulename, str):
raise ConfigError( raise ConfigError("expected a string", path=tuple(config_path) + ("module",))
"expected a string", path=itertools.chain(config_path, ("module",))
)
# We need to import the module, and then pick the class out of # We need to import the module, and then pick the class out of
# that, so we split based on the last dot. # that, so we split based on the last dot.
@ -55,19 +53,17 @@ def load_module(provider: dict, config_path: Iterable[str]) -> Tuple[Type, Any]:
try: try:
provider_config = provider_class.parse_config(module_config) provider_config = provider_class.parse_config(module_config)
except jsonschema.ValidationError as e: except jsonschema.ValidationError as e:
raise json_error_to_config_error( raise json_error_to_config_error(e, tuple(config_path) + ("config",))
e, itertools.chain(config_path, ("config",))
)
except ConfigError as e: except ConfigError as e:
raise _wrap_config_error( raise _wrap_config_error(
"Failed to parse config for module %r" % (modulename,), "Failed to parse config for module %r" % (modulename,),
prefix=itertools.chain(config_path, ("config",)), prefix=tuple(config_path) + ("config",),
e=e, e=e,
) )
except Exception as e: except Exception as e:
raise ConfigError( raise ConfigError(
"Failed to parse config for module %r" % (modulename,), "Failed to parse config for module %r" % (modulename,),
path=itertools.chain(config_path, ("config",)), path=tuple(config_path) + ("config",),
) from e ) from e
else: else:
provider_config = module_config provider_config = module_config
@ -92,9 +88,7 @@ def load_python_module(location: str) -> ModuleType:
return mod return mod
def _wrap_config_error( def _wrap_config_error(msg: str, prefix: StrSequence, e: ConfigError) -> "ConfigError":
msg: str, prefix: Iterable[str], e: ConfigError
) -> "ConfigError":
"""Wrap a relative ConfigError with a new path """Wrap a relative ConfigError with a new path
This is useful when we have a ConfigError with a relative path due to a problem This is useful when we have a ConfigError with a relative path due to a problem
@ -102,7 +96,7 @@ def _wrap_config_error(
""" """
path = prefix path = prefix
if e.path: if e.path:
path = itertools.chain(prefix, e.path) path = tuple(prefix) + tuple(e.path)
e1 = ConfigError(msg, path) e1 = ConfigError(msg, path)