Custom Exception classes
This commit is contained in:
parent
33cb62bc3a
commit
a54d66b9e7
4 changed files with 127 additions and 44 deletions
|
@ -6,6 +6,27 @@ from typing import List, Optional
|
|||
import click
|
||||
|
||||
|
||||
class MissingCMDObjectError(ValueError):
|
||||
"""raised if command object can't be found in its module"""
|
||||
pass
|
||||
|
||||
|
||||
class CMDObjectSubclassError(TypeError):
|
||||
"""raised if a command object is not inheriting click.Command"""
|
||||
pass
|
||||
|
||||
|
||||
class CMDUnregisteredError(ValueError):
|
||||
"""raised if commands have not been assigned to a command group"""
|
||||
|
||||
unregistered: List[str]
|
||||
|
||||
def __init__(self, unregistered):
|
||||
self.unregistered = unregistered
|
||||
|
||||
super().__init__(f"Some commands were not registered in a group above: {unregistered!r}")
|
||||
|
||||
|
||||
class KiwiCLI(click.MultiCommand):
|
||||
"""Command Line Interface spread over multiple files in this directory"""
|
||||
|
||||
|
@ -27,19 +48,19 @@ class KiwiCLI(click.MultiCommand):
|
|||
except ImportError:
|
||||
return
|
||||
|
||||
member_name = f"{cmd_name.capitalize()}Command"
|
||||
cmd_object_name = f"{cmd_name.capitalize()}Command"
|
||||
|
||||
if member_name in dir(cmd_module):
|
||||
member = getattr(cmd_module, member_name)
|
||||
if cmd_object_name in dir(cmd_module):
|
||||
cmd_object = getattr(cmd_module, cmd_object_name)
|
||||
|
||||
if isinstance(member, click.Command):
|
||||
return member
|
||||
if isinstance(cmd_object, click.Command):
|
||||
return cmd_object
|
||||
|
||||
else:
|
||||
raise Exception("Fail class")
|
||||
raise CMDObjectSubclassError()
|
||||
|
||||
else:
|
||||
raise Exception("Fail member name")
|
||||
raise MissingCMDObjectError()
|
||||
|
||||
def format_commands(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:
|
||||
commands = {
|
||||
|
@ -71,4 +92,4 @@ class KiwiCLI(click.MultiCommand):
|
|||
cmd_names -= set(cmd_list)
|
||||
|
||||
if len(cmd_names) > 0:
|
||||
raise Exception(f"Some commands were not registered in a group above: {cmd_names}")
|
||||
raise CMDUnregisteredError(cmd_names)
|
||||
|
|
|
@ -23,6 +23,10 @@ class KiwiCommandType(Enum):
|
|||
T = TypeVar("T")
|
||||
|
||||
|
||||
class KiwiCommandNotImplementedError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class KiwiCommand:
|
||||
type: KiwiCommandType = KiwiCommandType.SERVICES
|
||||
enabled_only: bool = False
|
||||
|
@ -177,4 +181,4 @@ class KiwiCommand:
|
|||
@classmethod
|
||||
def run_for_filtered_services(cls, instance: Instance, project: Project, services: Services,
|
||||
new_service_names: List[str], **kwargs) -> None:
|
||||
raise Exception
|
||||
raise KiwiCommandNotImplementedError()
|
||||
|
|
|
@ -9,6 +9,25 @@ from ._constants import RE_SEMVER, RE_VARNAME, KIWI_CONF_NAME, RESERVED_PROJECT_
|
|||
from .yaml import YAML
|
||||
|
||||
|
||||
class InvalidFormatError(ValueError):
|
||||
"""raised if format recognition unsuccessful"""
|
||||
|
||||
cls: type
|
||||
member: Optional[str]
|
||||
data: str
|
||||
|
||||
def __init__(self, cls, data, member = None):
|
||||
self.cls = cls
|
||||
self.data = data
|
||||
|
||||
if member is not None:
|
||||
self.member = member
|
||||
super().__init__(f"Invalid {self.cls.__name__!r}.{self.member!r} Format: {self.data!r}")
|
||||
|
||||
else:
|
||||
super().__init__(f"Invalid {self.cls.__name__!r} Format: {self.data!r}")
|
||||
|
||||
|
||||
class StorageConfig(BaseModel):
|
||||
"""a storage subsection"""
|
||||
|
||||
|
@ -31,7 +50,17 @@ class StorageConfig(BaseModel):
|
|||
|
||||
else:
|
||||
# undefined format
|
||||
raise ValueError("Invalid Storage Format")
|
||||
raise InvalidFormatError(cls, str(values))
|
||||
|
||||
|
||||
class ProjectNameReservedError(ValueError):
|
||||
"""raised if trying to create a project with a reserved name"""
|
||||
|
||||
name: str
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
super().__init__(f"Project name {self.name!r} is reserved!")
|
||||
|
||||
|
||||
class ProjectConfig(BaseModel):
|
||||
|
@ -58,7 +87,7 @@ class ProjectConfig(BaseModel):
|
|||
"""check if project name is allowed"""
|
||||
|
||||
if value in RESERVED_PROJECT_NAMES:
|
||||
raise ValueError(f"Project name '{value}' is reserved!")
|
||||
raise ProjectNameReservedError(value)
|
||||
|
||||
return value
|
||||
|
||||
|
@ -101,7 +130,7 @@ class ProjectConfig(BaseModel):
|
|||
|
||||
else:
|
||||
# undefined format
|
||||
raise ValueError("Invalid Project Format")
|
||||
raise InvalidFormatError(ProjectConfig, values)
|
||||
|
||||
|
||||
class NetworkConfig(BaseModel):
|
||||
|
@ -120,6 +149,18 @@ class NetworkConfig(BaseModel):
|
|||
}
|
||||
|
||||
|
||||
class MissingMemberError(ValueError):
|
||||
"""raised if class member is missing a definition"""
|
||||
|
||||
cls: type
|
||||
member: str
|
||||
|
||||
def __init__(self, cls, member):
|
||||
self.cls = cls
|
||||
self.member = member
|
||||
super().__init__(f"Member {self.cls.__name__!r}.{self.member!r} is required!")
|
||||
|
||||
|
||||
class KiwiConfig(BaseModel):
|
||||
"""represents a kiwi.yml"""
|
||||
|
||||
|
@ -225,7 +266,7 @@ class KiwiConfig(BaseModel):
|
|||
|
||||
except Exception:
|
||||
# undefined format
|
||||
raise ValueError("Invalid Shells Format")
|
||||
raise InvalidFormatError(KiwiConfig, value, "shells")
|
||||
|
||||
@validator("projects", pre=True)
|
||||
@classmethod
|
||||
|
@ -254,7 +295,7 @@ class KiwiConfig(BaseModel):
|
|||
|
||||
except Exception:
|
||||
# undefined format
|
||||
raise ValueError("Invalid Projects Format")
|
||||
raise InvalidFormatError(KiwiConfig, value, "projects")
|
||||
|
||||
return result
|
||||
|
||||
|
@ -270,7 +311,7 @@ class KiwiConfig(BaseModel):
|
|||
|
||||
except Exception:
|
||||
# undefined format
|
||||
raise ValueError("Invalid Projects Format")
|
||||
raise InvalidFormatError(KiwiConfig, value, "projects")
|
||||
|
||||
@validator("environment", pre=True)
|
||||
@classmethod
|
||||
|
@ -284,7 +325,7 @@ class KiwiConfig(BaseModel):
|
|||
idx = str(var_val).find("=")
|
||||
except Exception:
|
||||
# undefined format
|
||||
raise ValueError("Invalid Environment Format")
|
||||
raise InvalidFormatError(KiwiConfig, value, "environment")
|
||||
|
||||
if idx == -1:
|
||||
# don't split, just define the variable
|
||||
|
@ -325,7 +366,7 @@ class KiwiConfig(BaseModel):
|
|||
|
||||
if value is None:
|
||||
# empty storage
|
||||
raise ValueError("No Storage Given")
|
||||
raise MissingMemberError(KiwiConfig, "storage")
|
||||
|
||||
elif isinstance(value, dict):
|
||||
# native dict format
|
||||
|
@ -350,7 +391,7 @@ class KiwiConfig(BaseModel):
|
|||
|
||||
if value is None:
|
||||
# empty network
|
||||
raise ValueError("No Network Given")
|
||||
raise MissingMemberError(KiwiConfig, "network")
|
||||
|
||||
elif isinstance(value, dict):
|
||||
# native dict format
|
||||
|
@ -358,4 +399,4 @@ class KiwiConfig(BaseModel):
|
|||
|
||||
else:
|
||||
# undefined format
|
||||
raise ValueError("Invalid Network Format")
|
||||
raise InvalidFormatError(KiwiConfig, value, "network")
|
||||
|
|
|
@ -8,11 +8,18 @@ from kiwi_scp.config import KiwiConfig
|
|||
from kiwi_scp.yaml import YAML
|
||||
|
||||
|
||||
class UnCoercibleError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
class UnCoercible:
|
||||
"""A class that doesn't have a string representation"""
|
||||
|
||||
def __str__(self):
|
||||
raise ValueError
|
||||
raise UnCoercibleError()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "UnCoercible()"
|
||||
|
||||
|
||||
class TestDefault:
|
||||
|
@ -130,8 +137,8 @@ class TestShells:
|
|||
|
||||
assert len(exc_info.value.errors()) == 1
|
||||
error = exc_info.value.errors()[0]
|
||||
assert error["msg"] == "Invalid Shells Format"
|
||||
assert error["type"] == "value_error"
|
||||
assert error["msg"] == "Invalid 'KiwiConfig'.'shells' Format: UnCoercible()"
|
||||
assert error["type"] == "value_error.invalidformat"
|
||||
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
KiwiConfig(shells=["/bin/bash", UnCoercible()])
|
||||
|
@ -207,8 +214,8 @@ class TestProject:
|
|||
|
||||
assert len(exc_info.value.errors()) == 1
|
||||
error = exc_info.value.errors()[0]
|
||||
assert error["msg"] == "Invalid Storage Format"
|
||||
assert error["type"] == "value_error"
|
||||
assert error["msg"] == "Invalid 'StorageConfig' Format: '{}'"
|
||||
assert error["type"] == "value_error.invalidformat"
|
||||
|
||||
def test_short(self):
|
||||
kiwi_dict = {
|
||||
|
@ -239,6 +246,15 @@ class TestProject:
|
|||
assert p.enabled
|
||||
assert p.override_storage is None
|
||||
|
||||
def test_reserved_name(self):
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
KiwiConfig(projects={"name": "config"})
|
||||
|
||||
assert len(exc_info.value.errors()) == 1
|
||||
error = exc_info.value.errors()[0]
|
||||
assert error["msg"] == "Project name 'config' is reserved!"
|
||||
assert error["type"] == "value_error.projectnamereserved"
|
||||
|
||||
def test_invalid_dict(self):
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
KiwiConfig(projects={
|
||||
|
@ -248,8 +264,9 @@ class TestProject:
|
|||
|
||||
assert len(exc_info.value.errors()) == 1
|
||||
error = exc_info.value.errors()[0]
|
||||
assert error["msg"] == "Invalid Project Format"
|
||||
assert error["type"] == "value_error"
|
||||
assert error["msg"] == "Invalid 'ProjectConfig' Format: " \
|
||||
"{'random key 1': 'random value 1', 'random key 2': 'random value 2'}"
|
||||
assert error["type"] == "value_error.invalidformat"
|
||||
|
||||
def test_coercible(self):
|
||||
c = KiwiConfig(projects="project")
|
||||
|
@ -268,16 +285,16 @@ class TestProject:
|
|||
|
||||
assert len(exc_info.value.errors()) == 1
|
||||
error = exc_info.value.errors()[0]
|
||||
assert error["msg"] == "Invalid Projects Format"
|
||||
assert error["type"] == "value_error"
|
||||
assert error["msg"] == "Invalid 'KiwiConfig'.'projects' Format: ['valid', UnCoercible()]"
|
||||
assert error["type"] == "value_error.invalidformat"
|
||||
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
KiwiConfig(projects=UnCoercible())
|
||||
|
||||
assert len(exc_info.value.errors()) == 1
|
||||
error = exc_info.value.errors()[0]
|
||||
assert error["msg"] == "Invalid Projects Format"
|
||||
assert error["type"] == "value_error"
|
||||
assert error["msg"] == "Invalid 'KiwiConfig'.'projects' Format: UnCoercible()"
|
||||
assert error["type"] == "value_error.invalidformat"
|
||||
|
||||
|
||||
class TestEnvironment:
|
||||
|
@ -360,16 +377,16 @@ class TestEnvironment:
|
|||
|
||||
assert len(exc_info.value.errors()) == 1
|
||||
error = exc_info.value.errors()[0]
|
||||
assert error["msg"] == "Invalid Environment Format"
|
||||
assert error["type"] == "value_error"
|
||||
assert error["msg"] == "Invalid 'KiwiConfig'.'environment' Format: UnCoercible()"
|
||||
assert error["type"] == "value_error.invalidformat"
|
||||
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
KiwiConfig(environment=["valid", UnCoercible()])
|
||||
|
||||
assert len(exc_info.value.errors()) == 1
|
||||
error = exc_info.value.errors()[0]
|
||||
assert error["msg"] == "Invalid Environment Format"
|
||||
assert error["type"] == "value_error"
|
||||
assert error["msg"] == "Invalid 'KiwiConfig'.'environment' Format: None"
|
||||
assert error["type"] == "value_error.invalidformat"
|
||||
|
||||
|
||||
class TestStorage:
|
||||
|
@ -379,8 +396,8 @@ class TestStorage:
|
|||
|
||||
assert len(exc_info.value.errors()) == 1
|
||||
error = exc_info.value.errors()[0]
|
||||
assert error["msg"] == "No Storage Given"
|
||||
assert error["type"] == "value_error"
|
||||
assert error["msg"] == "Member 'KiwiConfig'.'storage' is required!"
|
||||
assert error["type"] == "value_error.missingmember"
|
||||
|
||||
def test_dict(self):
|
||||
kiwi_dict = {"directory": "/test/directory"}
|
||||
|
@ -395,8 +412,8 @@ class TestStorage:
|
|||
|
||||
assert len(exc_info.value.errors()) == 1
|
||||
error = exc_info.value.errors()[0]
|
||||
assert error["msg"] == "Invalid Storage Format"
|
||||
assert error["type"] == "value_error"
|
||||
assert error["msg"] == "Invalid 'StorageConfig' Format: \"{'random key': 'random value'}\""
|
||||
assert error["type"] == "value_error.invalidformat"
|
||||
|
||||
def test_str(self):
|
||||
c = KiwiConfig(storage="/test/directory")
|
||||
|
@ -414,8 +431,8 @@ class TestStorage:
|
|||
|
||||
assert len(exc_info.value.errors()) == 1
|
||||
error = exc_info.value.errors()[0]
|
||||
assert error["msg"] == "Invalid Storage Format"
|
||||
assert error["type"] == "value_error"
|
||||
assert error["msg"] == "Invalid 'StorageConfig' Format: '{}'"
|
||||
assert error["type"] == "value_error.invalidformat"
|
||||
|
||||
|
||||
class TestNetwork:
|
||||
|
@ -425,8 +442,8 @@ class TestNetwork:
|
|||
|
||||
assert len(exc_info.value.errors()) == 1
|
||||
error = exc_info.value.errors()[0]
|
||||
assert error["msg"] == "No Network Given"
|
||||
assert error["type"] == "value_error"
|
||||
assert error["msg"] == "Member 'KiwiConfig'.'network' is required!"
|
||||
assert error["type"] == "value_error.missingmember"
|
||||
|
||||
def test_dict(self):
|
||||
kiwi_dict = {
|
||||
|
@ -470,5 +487,5 @@ class TestNetwork:
|
|||
|
||||
assert len(exc_info.value.errors()) == 1
|
||||
error = exc_info.value.errors()[0]
|
||||
assert error["msg"] == "Invalid Network Format"
|
||||
assert error["type"] == "value_error"
|
||||
assert error["msg"] == "Invalid 'KiwiConfig'.'network' Format: True"
|
||||
assert error["type"] == "value_error.invalidformat"
|
||||
|
|
Loading…
Reference in a new issue