Skip to content

Commit

Permalink
Make error codes more specific
Browse files Browse the repository at this point in the history
- also add a test to test all example cards
  • Loading branch information
e-lo committed Dec 18, 2023
1 parent 59ec7f7 commit e8cf9a8
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 56 deletions.
2 changes: 1 addition & 1 deletion projectcard/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .io import read_cards, read_card,write_card
from .logger import CardLogger, setup_logging
from .projectcard import ProjectCard, SubProject
from .validate import validate_card, validate_schema_file, PycodeError
from .validate import validate_card, validate_schema_file, PycodeError, ValidationError
10 changes: 6 additions & 4 deletions projectcard/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from .logger import CardLogger
from .projectcard import REPLACE_KEYS, VALID_EXT, ProjectCard

class ProjectCardReadError(Exception):
pass

def _get_cardpath_list(filepath, valid_ext: Collection[str] = VALID_EXT):
"""Returns a list of valid paths to project cards given a search string.
Expand All @@ -36,7 +38,7 @@ def _get_cardpath_list(filepath, valid_ext: Collection[str] = VALID_EXT):
CardLogger.debug(f"Getting all files in: {filepath}")
_paths = [Path(p) for p in Path(filepath).glob("*")]
else:
raise ValueError(f"filepath: {filepath} not understood.")
raise ProjectCardReadError(f"filepath: {filepath} not understood.")
CardLogger.debug(f"All paths: {_paths}")
_card_paths = [p for p in _paths if p.suffix in valid_ext]
CardLogger.debug(f"Reading set of paths: {_card_paths}")
Expand Down Expand Up @@ -159,7 +161,7 @@ def read_card(filepath: str, validate:bool = False):
validate: if True, will validate the project card schemea
"""
if not Path(filepath).is_file():
raise ValueError(f"Cannot find project card file: {filepath}")
raise FileNotFoundError(f"Cannot find project card file: {filepath}")
card_dict = read_cards(filepath,_cards={})
card = list(card_dict.values())[0]
if validate:
Expand Down Expand Up @@ -194,13 +196,13 @@ def read_cards(
_ext = os.path.splitext(filepath)[1]
if _ext not in _read_method_map.keys():
CardLogger.debug(f"Unsupported file type for file {filepath}")
raise ValueError(f"Unsupported file type: {_ext}")
raise ProjectCardReadError(f"Unsupported file type: {_ext}")
_card_dict = _read_method_map[_ext](filepath)
_card_dict = _change_keys(_card_dict)
_card_dict["file"] = filepath
_project_name = _card_dict["project"].lower()
if _project_name in _cards:
raise ValueError(
raise ProjectCardReadError(
f"Names not unique from existing scenario projects: {_project_name}"
)
if filter_tags and set(list(map(str.lower, _card_dict.get("tags", [])))).isdisjoint(
Expand Down
45 changes: 14 additions & 31 deletions projectcard/projectcard.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Union, List

from .logger import CardLogger
from .validate import validate_card
from .validate import validate_card, SubprojectValidationError, ProjectCardValidationError
from .utils import findkeys

UNSPECIFIED_PROJECT_NAMES = ["", "TO DO User Define", "USER TO define"]
Expand Down Expand Up @@ -59,7 +59,7 @@ def facilities(self) -> List[dict]:
return []
f = list(findkeys(self.__dict__,"facility"))
if not f:
raise ValueError("Couldn't find facility in project card")
raise ProjectCardValidationError("Couldn't find facility in project card")
return f

@property
Expand All @@ -76,7 +76,7 @@ def services(self) -> List[dict]:
return []
s = list(findkeys(self.__dict__,"service"))
if not s:
raise ValueError("Couldn't find service in project card")
raise ProjectCardValidationError("Couldn't find service in project card")
return s

@property
Expand All @@ -86,26 +86,10 @@ def service(self) -> Union[str,dict]:
return "multiple"
return s[0]

@property
def all_roadway_property_changes(self) -> List[dict]:
if not any(["roadway_property_change" in t for t in self.types]):
CardLogger.warning("Project doesn't have roadway_property changes.")
return []
rp = list(findkeys(self.__dict__,"roadway_property_change"))
p = [i["property_changes"] for i in rp]
return p

@property
def roadway_property_change(self) -> Union[str,dict]:
p = self.all_roadway_property_changes
if len(p) > 1:
return "multiple"
return p[0]

@property
def all_transit_property_changes(self) -> List[dict]:
if not any(["transit_property_change" in t for t in self.types]):
CardLogger.warning("Project doesn't have transit property changes.")
CardLogger.warning(f"Project {self.project} doesn't have transit property changes.")
return []
tp = list(findkeys(self.__dict__,"transit_property_change"))
p = [i["property_changes"] for i in tp]
Expand All @@ -121,7 +105,7 @@ def transit_property_change(self) -> Union[str,dict]:
@property
def all_transit_routing_changes(self) -> List[dict]:
if not any(["transit_routing_change" in t for t in self.types]):
CardLogger.warning("Project doesn't have routing changes.")
CardLogger.warning(f"Project {self.project} doesn't have routing changes.")
return []
r = list(findkeys(self.__dict__,"routing"))
CardLogger.debug(f"transit routing change: {r}")
Expand All @@ -141,7 +125,7 @@ def types(self) -> List[str]:
_ignore = ["project","tags","notes","dependencies","self","sub_projects","file"]
type_keys = [k for k in self.__dict__.keys() if k not in _ignore]
if not type_keys:
raise ValueError("Couldn't find type of project card")
raise ProjectCardValidationError(f"Couldn't find type of project card {self.project}")
return type_keys

@property
Expand Down Expand Up @@ -175,9 +159,11 @@ def __init__(self, sp_dictionary: dict, parent_project: ProjectCard):
"""
self.parent_project = parent_project

assert len(sp_dictionary) == 1
if not len(sp_dictionary) == 1:
raise SubprojectValidationError(f"Subproject of {parent_project.project} \
should only have one change. Did you forget to indent the rest of this change?.")
self._type = list(sp_dictionary.keys())[0]
self.__dict__.update(sp_dictionary[self.type])
self.__dict__.update(sp_dictionary)
self.sub_projects = []

@property
Expand All @@ -197,15 +183,12 @@ def dependencies(self) -> str:
def tags(self) -> str:
return self.parent_project.tags

@property
def property_changes(self) -> dict:
f = self.__dict__.get("property_changes",{})
return f

@property
def facility(self) -> dict:
f = self.__dict__.get("facility",{})
return f
if not "facility" in self.__dict__:
raise SubprojectValidationError(f"Couldn't find facility in subproject in project card\
{self.parent_project.project}")
return self.__dict__["facility"]

@property
def valid(self) -> bool:
Expand Down
31 changes: 20 additions & 11 deletions projectcard/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@
# F405 name may be undefined, or defined from star imports: module
FLAKE8_ERRORS = ["E9", "F821", "F823", "F405"]

class ProjectCardValidationError(ValidationError):
"Error in formatting of ProjectCard."
pass
class SubprojectValidationError(ProjectCardValidationError):
"Error in formatting of Subproject."
pass
class PycodeError(ProjectCardValidationError):
"Basic runtime error in python code."
pass

class ProjectCardJSONSchemaError(SchemaError):
"Error in the ProjectCard json schema"
pass


def _open_json(schema_path: str) -> dict:
try:
Expand All @@ -30,10 +44,10 @@ def _open_json(schema_path: str) -> dict:
return _json
except FileNotFoundError:
CardLogger.error(f"Schema not found: {schema_path}")
raise Exception("Schema definition missing")
raise ProjectCardJSONSchemaError("Schema definition missing")
except JSONDecodeError:
CardLogger.error(f"Schema not valid JSON: {schema_path}")
raise Exception("Schema definition invalid")
raise ProjectCardJSONSchemaError("Schema definition invalid")


def _load_schema(schema_absolute_path: Union[Path, str]) -> dict:
Expand Down Expand Up @@ -90,7 +104,7 @@ def validate_schema_file(schema_path: Union[Path, str] = PROJECTCARD_SCHEMA) ->
pass
except SchemaError as e:
CardLogger.error(e)
raise SchemaError(f"{e}")
raise ProjectCardJSONSchemaError(f"{e}")

return True

Expand Down Expand Up @@ -123,25 +137,20 @@ def validate_card(
msg = f"\nRelevant schema: {e.schema}\nValidator Value: {e.validator_value}\nValidator: {e.validator}"
msg += f"\nabsolute_schema_path:{e.absolute_schema_path}\nabsolute_path:{e.absolute_path}"
CardLogger.error(msg)
raise ValidationError(f"{e}")
raise ProjectCardValidationError(f"{e}")
except SchemaError as e:
CardLogger.error(e)
raise SchemaError(f"{e}")
raise ProjectCardJSONSchemaError(f"{e}")

if "pycode" in jsondata:
if "self." in jsondata["pycode"]:
if not "self" in jsondata:
raise ValidationError("If using self, must specify what `self` refers to in yml frontmatter")
raise PycodeError("If using self, must specify what `self` refers to in yml frontmatter")
_validate_pycode(jsondata)

return True


class PycodeError(Exception):
"Basic runtime error in python code."
pass


def _validate_pycode(jsondata: dict,mocked_vars:List[str]=["self","roadway_net","transit_net"]) -> None:
"""Use flake8 to evaluate basic runtime errors on pycode.
Expand Down
9 changes: 0 additions & 9 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,6 @@ def example_dir(base_dir):
return os.path.join(base_dir, "examples")


@pytest.fixture(scope="module")
def all_example_cards(example_dir):
from projectcard import read_cards

CardLogger.info("Reading cards from example directory.")
cards = read_cards(example_dir)
return cards


@pytest.fixture(scope="session")
def all_bad_card_files(test_dir):
"""Card files which should fail"""
Expand Down

0 comments on commit e8cf9a8

Please sign in to comment.