added generic rule generation
This commit is contained in:
@ -1,4 +1,5 @@
|
|||||||
|
|
||||||
|
from abc import ABCMeta
|
||||||
from typing import Any, _SpecialForm
|
from typing import Any, _SpecialForm
|
||||||
|
|
||||||
def check_input(variable:Any, expected_type:type, alt_types:list[type]=[],
|
def check_input(variable:Any, expected_type:type, alt_types:list[type]=[],
|
||||||
@ -70,14 +71,18 @@ def valid_string(variable:str, valid_chars:str, min_length:int=1)->None:
|
|||||||
|
|
||||||
def valid_dict(variable:dict[Any, Any], key_type:type, value_type:type,
|
def valid_dict(variable:dict[Any, Any], key_type:type, value_type:type,
|
||||||
required_keys:list[Any]=[], optional_keys:list[Any]=[],
|
required_keys:list[Any]=[], optional_keys:list[Any]=[],
|
||||||
strict:bool=True)->None:
|
strict:bool=True, min_length:int=1)->None:
|
||||||
check_input(variable, dict)
|
check_input(variable, dict)
|
||||||
check_input(key_type, type, alt_types=[_SpecialForm])
|
check_input(key_type, type, alt_types=[_SpecialForm, ABCMeta])
|
||||||
check_input(value_type, type, alt_types=[_SpecialForm])
|
check_input(value_type, type, alt_types=[_SpecialForm, ABCMeta])
|
||||||
check_input(required_keys, list)
|
check_input(required_keys, list)
|
||||||
check_input(optional_keys, list)
|
check_input(optional_keys, list)
|
||||||
check_input(strict, bool)
|
check_input(strict, bool)
|
||||||
|
|
||||||
|
if len(variable) < min_length:
|
||||||
|
raise ValueError(f"Dictionary '{variable}' is below minimum length of "
|
||||||
|
f"{min_length}")
|
||||||
|
|
||||||
for k, v in variable.items():
|
for k, v in variable.items():
|
||||||
if key_type != Any and not isinstance(k, key_type):
|
if key_type != Any and not isinstance(k, key_type):
|
||||||
raise TypeError(f"Key {k} had unexpected type '{type(k)}' "
|
raise TypeError(f"Key {k} had unexpected type '{type(k)}' "
|
||||||
@ -96,3 +101,12 @@ def valid_dict(variable:dict[Any, Any], key_type:type, value_type:type,
|
|||||||
if k not in required_keys and k not in optional_keys:
|
if k not in required_keys and k not in optional_keys:
|
||||||
raise ValueError(f"Unexpected key '{k}' should not be present "
|
raise ValueError(f"Unexpected key '{k}' should not be present "
|
||||||
f"in dict '{variable}'")
|
f"in dict '{variable}'")
|
||||||
|
|
||||||
|
def valid_list(variable:list[Any], entry_type:type,
|
||||||
|
alt_types:list[type]=[], min_length:int=1)->None:
|
||||||
|
check_input(variable, list)
|
||||||
|
if len(variable) < min_length:
|
||||||
|
raise ValueError(f"List '{variable}' is too short. Should be at least "
|
||||||
|
f"of length {min_length}")
|
||||||
|
for entry in variable:
|
||||||
|
check_input(entry, entry_type, alt_types=alt_types)
|
||||||
|
78
core/functionality.py
Normal file
78
core/functionality.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
import sys
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
from typing import Union
|
||||||
|
from random import SystemRandom
|
||||||
|
|
||||||
|
from core.meow import BasePattern, BaseRecipe, BaseRule
|
||||||
|
from core.correctness.validation import check_input, valid_dict, valid_list
|
||||||
|
from core.correctness.vars import CHAR_LOWERCASE, CHAR_UPPERCASE
|
||||||
|
from patterns import *
|
||||||
|
from recipes import *
|
||||||
|
from rules import *
|
||||||
|
|
||||||
|
def check_pattern_dict(patterns, min_length=1):
|
||||||
|
valid_dict(patterns, str, BasePattern, strict=False, min_length=min_length)
|
||||||
|
for k, v in patterns.items():
|
||||||
|
if k != v.name:
|
||||||
|
raise KeyError(f"Key '{k}' indexes unexpected Pattern '{v.name}' "
|
||||||
|
"Pattern dictionaries must be keyed with the name of the "
|
||||||
|
"Pattern.")
|
||||||
|
|
||||||
|
def check_recipe_dict(recipes, min_length=1):
|
||||||
|
valid_dict(recipes, str, BaseRecipe, strict=False, min_length=min_length)
|
||||||
|
for k, v in recipes.items():
|
||||||
|
if k != v.name:
|
||||||
|
raise KeyError(f"Key '{k}' indexes unexpected Recipe '{v.name}' "
|
||||||
|
"Recipe dictionaries must be keyed with the name of the "
|
||||||
|
"Recipe.")
|
||||||
|
|
||||||
|
def generate_id(prefix:str="", length:int=16, existing_ids:list[str]=[],
|
||||||
|
charset:str=CHAR_UPPERCASE+CHAR_LOWERCASE, attempts:int=24):
|
||||||
|
random_length = max(length - len(prefix), 0)
|
||||||
|
for _ in range(attempts):
|
||||||
|
id = prefix + ''.join(SystemRandom().choice(charset)
|
||||||
|
for _ in range(random_length))
|
||||||
|
if id not in existing_ids:
|
||||||
|
return id
|
||||||
|
raise ValueError(f"Could not generate ID unique from '{existing_ids}' "
|
||||||
|
f"using values '{charset}' and length of '{length}'.")
|
||||||
|
|
||||||
|
def create_rules(patterns:Union[dict[str,BasePattern],list[BasePattern]],
|
||||||
|
recipes:Union[dict[str,BaseRecipe],list[BaseRecipe]],
|
||||||
|
new_rules:list[BaseRule]=[])->dict[str,BaseRule]:
|
||||||
|
check_input(patterns, dict, alt_types=[list])
|
||||||
|
check_input(recipes, dict, alt_types=[list])
|
||||||
|
valid_list(new_rules, BaseRule, min_length=0)
|
||||||
|
|
||||||
|
if isinstance(patterns, list):
|
||||||
|
valid_list(patterns, BasePattern, min_length=0)
|
||||||
|
patterns = {pattern.name:pattern for pattern in patterns}
|
||||||
|
else:
|
||||||
|
check_pattern_dict(patterns, min_length=0)
|
||||||
|
|
||||||
|
if isinstance(recipes, list):
|
||||||
|
valid_list(recipes, BaseRecipe, min_length=0)
|
||||||
|
recipes = {recipe.name:recipe for recipe in recipes}
|
||||||
|
else:
|
||||||
|
check_recipe_dict(recipes, min_length=0)
|
||||||
|
|
||||||
|
rules = {}
|
||||||
|
|
||||||
|
all_rules ={(r.pattern_type, r.recipe_type):r for r in [r[1] \
|
||||||
|
for r in inspect.getmembers(sys.modules["rules"], inspect.isclass) \
|
||||||
|
if (issubclass(r[1], BaseRule))]}
|
||||||
|
|
||||||
|
for pattern in patterns.values():
|
||||||
|
if pattern.recipe in recipes:
|
||||||
|
key = (type(pattern).__name__,
|
||||||
|
type(recipes[pattern.recipe]).__name__)
|
||||||
|
if (key) in all_rules:
|
||||||
|
rule = all_rules[key](
|
||||||
|
generate_id(prefix="Rule_"),
|
||||||
|
pattern,
|
||||||
|
recipes[pattern.recipe]
|
||||||
|
)
|
||||||
|
rules[rule.name] = rule
|
||||||
|
return rules
|
66
core/meow.py
66
core/meow.py
@ -2,6 +2,8 @@
|
|||||||
import core.correctness.vars
|
import core.correctness.vars
|
||||||
import core.correctness.validation
|
import core.correctness.validation
|
||||||
|
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
class BaseRecipe:
|
class BaseRecipe:
|
||||||
@ -20,21 +22,6 @@ class BaseRecipe:
|
|||||||
self._is_valid_requirements(requirements)
|
self._is_valid_requirements(requirements)
|
||||||
self.requirements = requirements
|
self.requirements = requirements
|
||||||
|
|
||||||
def __init_subclass__(cls, **kwargs) -> None:
|
|
||||||
if cls._is_valid_recipe == BaseRecipe._is_valid_recipe:
|
|
||||||
raise NotImplementedError(
|
|
||||||
f"Recipe '{cls.__name__}' has not implemented "
|
|
||||||
"'_is_valid_recipe(self, recipe)' function.")
|
|
||||||
if cls._is_valid_parameters == BaseRecipe._is_valid_parameters:
|
|
||||||
raise NotImplementedError(
|
|
||||||
f"Recipe '{cls.__name__}' has not implemented "
|
|
||||||
"'_is_valid_parameters(self, parameters)' function.")
|
|
||||||
if cls._is_valid_requirements == BaseRecipe._is_valid_requirements:
|
|
||||||
raise NotImplementedError(
|
|
||||||
f"Recipe '{cls.__name__}' has not implemented "
|
|
||||||
"'_is_valid_requirements(self, requirements)' function.")
|
|
||||||
super().__init_subclass__(**kwargs)
|
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
if cls is BaseRecipe:
|
if cls is BaseRecipe:
|
||||||
raise TypeError("BaseRecipe may not be instantiated directly")
|
raise TypeError("BaseRecipe may not be instantiated directly")
|
||||||
@ -44,17 +31,20 @@ class BaseRecipe:
|
|||||||
core.correctness.validation.valid_string(
|
core.correctness.validation.valid_string(
|
||||||
name, core.correctness.vars.VALID_RECIPE_NAME_CHARS)
|
name, core.correctness.vars.VALID_RECIPE_NAME_CHARS)
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
def _is_valid_recipe(self, recipe:Any)->None:
|
def _is_valid_recipe(self, recipe:Any)->None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
def _is_valid_parameters(self, parameters:Any)->None:
|
def _is_valid_parameters(self, parameters:Any)->None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
def _is_valid_requirements(self, requirements:Any)->None:
|
def _is_valid_requirements(self, requirements:Any)->None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BasePattern:
|
class BasePattern(ABC):
|
||||||
name:str
|
name:str
|
||||||
recipe:str
|
recipe:str
|
||||||
parameters:dict[str, Any]
|
parameters:dict[str, Any]
|
||||||
@ -70,21 +60,6 @@ class BasePattern:
|
|||||||
self._is_valid_output(outputs)
|
self._is_valid_output(outputs)
|
||||||
self.outputs = outputs
|
self.outputs = outputs
|
||||||
|
|
||||||
def __init_subclass__(cls, **kwargs) -> None:
|
|
||||||
if cls._is_valid_recipe == BasePattern._is_valid_recipe:
|
|
||||||
raise NotImplementedError(
|
|
||||||
f"Pattern '{cls.__name__}' has not implemented "
|
|
||||||
"'_is_valid_recipe(self, recipe)' function.")
|
|
||||||
if cls._is_valid_parameters == BasePattern._is_valid_parameters:
|
|
||||||
raise NotImplementedError(
|
|
||||||
f"Pattern '{cls.__name__}' has not implemented "
|
|
||||||
"'_is_valid_parameters(self, parameters)' function.")
|
|
||||||
if cls._is_valid_output == BasePattern._is_valid_output:
|
|
||||||
raise NotImplementedError(
|
|
||||||
f"Pattern '{cls.__name__}' has not implemented "
|
|
||||||
"'_is_valid_output(self, outputs)' function.")
|
|
||||||
super().__init_subclass__(**kwargs)
|
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
if cls is BasePattern:
|
if cls is BasePattern:
|
||||||
raise TypeError("BasePattern may not be instantiated directly")
|
raise TypeError("BasePattern may not be instantiated directly")
|
||||||
@ -94,20 +69,25 @@ class BasePattern:
|
|||||||
core.correctness.validation.valid_string(
|
core.correctness.validation.valid_string(
|
||||||
name, core.correctness.vars.VALID_PATTERN_NAME_CHARS)
|
name, core.correctness.vars.VALID_PATTERN_NAME_CHARS)
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
def _is_valid_recipe(self, recipe:Any)->None:
|
def _is_valid_recipe(self, recipe:Any)->None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
def _is_valid_parameters(self, parameters:Any)->None:
|
def _is_valid_parameters(self, parameters:Any)->None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
def _is_valid_output(self, outputs:Any)->None:
|
def _is_valid_output(self, outputs:Any)->None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BaseRule:
|
class BaseRule(ABC):
|
||||||
name:str
|
name:str
|
||||||
pattern:BasePattern
|
pattern:BasePattern
|
||||||
recipe:BaseRecipe
|
recipe:BaseRecipe
|
||||||
|
pattern_type:str=""
|
||||||
|
recipe_type:str=""
|
||||||
def __init__(self, name:str, pattern:BasePattern, recipe:BaseRecipe):
|
def __init__(self, name:str, pattern:BasePattern, recipe:BaseRecipe):
|
||||||
self._is_valid_name(name)
|
self._is_valid_name(name)
|
||||||
self.name = name
|
self.name = name
|
||||||
@ -115,29 +95,29 @@ class BaseRule:
|
|||||||
self.pattern = pattern
|
self.pattern = pattern
|
||||||
self._is_valid_recipe(recipe)
|
self._is_valid_recipe(recipe)
|
||||||
self.recipe = recipe
|
self.recipe = recipe
|
||||||
|
self.__check_types_set()
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
if cls is BaseRule:
|
if cls is BaseRule:
|
||||||
raise TypeError("BaseRule may not be instantiated directly")
|
raise TypeError("BaseRule may not be instantiated directly")
|
||||||
return object.__new__(cls)
|
return object.__new__(cls)
|
||||||
|
|
||||||
def __init_subclass__(cls, **kwargs) -> None:
|
|
||||||
if cls._is_valid_pattern == BaseRule._is_valid_pattern:
|
|
||||||
raise NotImplementedError(
|
|
||||||
f"Rule '{cls.__name__}' has not implemented "
|
|
||||||
"'_is_valid_pattern(self, pattern)' function.")
|
|
||||||
if cls._is_valid_recipe == BaseRule._is_valid_recipe:
|
|
||||||
raise NotImplementedError(
|
|
||||||
f"Pattern '{cls.__name__}' has not implemented "
|
|
||||||
"'_is_valid_recipe(self, recipe)' function.")
|
|
||||||
super().__init_subclass__(**kwargs)
|
|
||||||
|
|
||||||
def _is_valid_name(self, name:str)->None:
|
def _is_valid_name(self, name:str)->None:
|
||||||
core.correctness.validation.valid_string(
|
core.correctness.validation.valid_string(
|
||||||
name, core.correctness.vars.VALID_RULE_NAME_CHARS)
|
name, core.correctness.vars.VALID_RULE_NAME_CHARS)
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
def _is_valid_pattern(self, pattern:Any)->None:
|
def _is_valid_pattern(self, pattern:Any)->None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
def _is_valid_recipe(self, recipe:Any)->None:
|
def _is_valid_recipe(self, recipe:Any)->None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def __check_types_set(self)->None:
|
||||||
|
if self.pattern_type == "":
|
||||||
|
raise AttributeError(f"Rule Class '{self.__class__.__name__}' "
|
||||||
|
"does not set a pattern_type.")
|
||||||
|
if self.recipe_type == "":
|
||||||
|
raise AttributeError(f"Rule Class '{self.__class__.__name__}' "
|
||||||
|
"does not set a recipe_type.")
|
||||||
|
2
patterns/__init__.py
Normal file
2
patterns/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
from patterns.file_event_pattern import FileEventPattern
|
@ -34,11 +34,11 @@ class FileEventPattern(BasePattern):
|
|||||||
valid_string(triggering_file, VALID_VARIABLE_NAME_CHARS)
|
valid_string(triggering_file, VALID_VARIABLE_NAME_CHARS)
|
||||||
|
|
||||||
def _is_valid_parameters(self, parameters:dict[str,Any])->None:
|
def _is_valid_parameters(self, parameters:dict[str,Any])->None:
|
||||||
valid_dict(parameters, str, Any, strict=False)
|
valid_dict(parameters, str, Any, strict=False, min_length=0)
|
||||||
for k in parameters.keys():
|
for k in parameters.keys():
|
||||||
valid_string(k, VALID_VARIABLE_NAME_CHARS)
|
valid_string(k, VALID_VARIABLE_NAME_CHARS)
|
||||||
|
|
||||||
def _is_valid_output(self, outputs:dict[str,str])->None:
|
def _is_valid_output(self, outputs:dict[str,str])->None:
|
||||||
valid_dict(outputs, str, str, strict=False)
|
valid_dict(outputs, str, str, strict=False, min_length=0)
|
||||||
for k in outputs.keys():
|
for k in outputs.keys():
|
||||||
valid_string(k, VALID_VARIABLE_NAME_CHARS)
|
valid_string(k, VALID_VARIABLE_NAME_CHARS)
|
||||||
|
2
recipes/__init__.py
Normal file
2
recipes/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
from recipes.jupyter_notebook_recipe import JupyterNotebookRecipe
|
@ -36,11 +36,11 @@ class JupyterNotebookRecipe(BaseRecipe):
|
|||||||
nbformat.validate(recipe)
|
nbformat.validate(recipe)
|
||||||
|
|
||||||
def _is_valid_parameters(self, parameters:dict[str,Any])->None:
|
def _is_valid_parameters(self, parameters:dict[str,Any])->None:
|
||||||
valid_dict(parameters, str, Any, strict=False)
|
valid_dict(parameters, str, Any, strict=False, min_length=0)
|
||||||
for k in parameters.keys():
|
for k in parameters.keys():
|
||||||
valid_string(k, VALID_VARIABLE_NAME_CHARS)
|
valid_string(k, VALID_VARIABLE_NAME_CHARS)
|
||||||
|
|
||||||
def _is_valid_requirements(self, requirements:dict[str,Any])->None:
|
def _is_valid_requirements(self, requirements:dict[str,Any])->None:
|
||||||
valid_dict(requirements, str, Any, strict=False)
|
valid_dict(requirements, str, Any, strict=False, min_length=0)
|
||||||
for k in requirements.keys():
|
for k in requirements.keys():
|
||||||
valid_string(k, VALID_VARIABLE_NAME_CHARS)
|
valid_string(k, VALID_VARIABLE_NAME_CHARS)
|
||||||
|
2
rules/__init__.py
Normal file
2
rules/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
from rules.file_event_jupyter_notebook_rule import FileEventJupyterNotebookRule
|
@ -5,6 +5,8 @@ from patterns.file_event_pattern import FileEventPattern
|
|||||||
from recipes.jupyter_notebook_recipe import JupyterNotebookRecipe
|
from recipes.jupyter_notebook_recipe import JupyterNotebookRecipe
|
||||||
|
|
||||||
class FileEventJupyterNotebookRule(BaseRule):
|
class FileEventJupyterNotebookRule(BaseRule):
|
||||||
|
pattern_type = "FileEventPattern"
|
||||||
|
recipe_type = "JupyterNotebookRecipe"
|
||||||
def __init__(self, name: str, pattern:FileEventPattern,
|
def __init__(self, name: str, pattern:FileEventPattern,
|
||||||
recipe:JupyterNotebookRecipe):
|
recipe:JupyterNotebookRecipe):
|
||||||
super().__init__(name, pattern, recipe)
|
super().__init__(name, pattern, recipe)
|
||||||
@ -18,3 +20,6 @@ class FileEventJupyterNotebookRule(BaseRule):
|
|||||||
|
|
||||||
def _is_valid_recipe(self, recipe:JupyterNotebookRecipe) -> None:
|
def _is_valid_recipe(self, recipe:JupyterNotebookRecipe) -> None:
|
||||||
check_input(recipe, JupyterNotebookRecipe)
|
check_input(recipe, JupyterNotebookRecipe)
|
||||||
|
|
||||||
|
def _set_pattern_type(self)->None:
|
||||||
|
self.pattern_type
|
||||||
|
95
tests/test_functionality.py
Normal file
95
tests/test_functionality.py
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from core.correctness.vars import CHAR_LOWERCASE, CHAR_UPPERCASE
|
||||||
|
from core.functionality import create_rules, generate_id
|
||||||
|
from core.meow import BaseRule
|
||||||
|
from patterns.file_event_pattern import FileEventPattern
|
||||||
|
from recipes.jupyter_notebook_recipe import JupyterNotebookRecipe
|
||||||
|
|
||||||
|
BAREBONES_NOTEBOOK = {
|
||||||
|
"cells": [],
|
||||||
|
"metadata": {},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 4
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_pattern_one = FileEventPattern(
|
||||||
|
"pattern_one", "path_one", "recipe_one", "file_one")
|
||||||
|
valid_pattern_two = FileEventPattern(
|
||||||
|
"pattern_two", "path_two", "recipe_two", "file_two")
|
||||||
|
|
||||||
|
valid_recipe_one = JupyterNotebookRecipe(
|
||||||
|
"recipe_one", BAREBONES_NOTEBOOK)
|
||||||
|
valid_recipe_two = JupyterNotebookRecipe(
|
||||||
|
"recipe_two", BAREBONES_NOTEBOOK)
|
||||||
|
|
||||||
|
class CorrectnessTests(unittest.TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
return super().setUp()
|
||||||
|
|
||||||
|
def tearDown(self) -> None:
|
||||||
|
return super().tearDown()
|
||||||
|
|
||||||
|
def testCreateRulesMinimum(self)->None:
|
||||||
|
create_rules({}, {})
|
||||||
|
|
||||||
|
def testGenerateIDWorking(self)->None:
|
||||||
|
id = generate_id()
|
||||||
|
self.assertEqual(len(id), 16)
|
||||||
|
for i in range(len(id)):
|
||||||
|
self.assertIn(id[i], CHAR_UPPERCASE+CHAR_LOWERCASE)
|
||||||
|
|
||||||
|
# In extrememly rare cases this may fail due to randomness in algorithm
|
||||||
|
new_id = generate_id(existing_ids=[id])
|
||||||
|
self.assertNotEqual(id, new_id)
|
||||||
|
|
||||||
|
another_id = generate_id(length=32)
|
||||||
|
self.assertEqual(len(another_id), 32)
|
||||||
|
|
||||||
|
again_id = generate_id(charset="a")
|
||||||
|
for i in range(len(again_id)):
|
||||||
|
self.assertIn(again_id[i], "a")
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
generate_id(length=2, charset="a", existing_ids=["aa"])
|
||||||
|
|
||||||
|
prefix_id = generate_id(length=4, prefix="Test")
|
||||||
|
self.assertEqual(prefix_id, "Test")
|
||||||
|
|
||||||
|
prefix_id = generate_id(prefix="Test")
|
||||||
|
self.assertEqual(len(prefix_id), 16)
|
||||||
|
self.assertTrue(prefix_id.startswith("Test"))
|
||||||
|
|
||||||
|
def testCreateRulesPatternsAndRecipesDicts(self)->None:
|
||||||
|
patterns = {
|
||||||
|
valid_pattern_one.name: valid_pattern_one,
|
||||||
|
valid_pattern_two.name: valid_pattern_two
|
||||||
|
}
|
||||||
|
recipes = {
|
||||||
|
valid_recipe_one.name: valid_recipe_one,
|
||||||
|
valid_recipe_two.name: valid_recipe_two
|
||||||
|
}
|
||||||
|
rules = create_rules(patterns, recipes)
|
||||||
|
self.assertIsInstance(rules, dict)
|
||||||
|
self.assertEqual(len(rules), 2)
|
||||||
|
for k, rule in rules.items():
|
||||||
|
self.assertIsInstance(k, str)
|
||||||
|
self.assertIsInstance(rule, BaseRule)
|
||||||
|
self.assertEqual(k, rule.name)
|
||||||
|
|
||||||
|
def testCreateRulesMisindexedPatterns(self)->None:
|
||||||
|
patterns = {
|
||||||
|
valid_pattern_two.name: valid_pattern_one,
|
||||||
|
valid_pattern_one.name: valid_pattern_two
|
||||||
|
}
|
||||||
|
with self.assertRaises(KeyError):
|
||||||
|
create_rules(patterns, {})
|
||||||
|
|
||||||
|
def testCreateRulesMisindexedRecipes(self)->None:
|
||||||
|
recipes = {
|
||||||
|
valid_recipe_two.name: valid_recipe_one,
|
||||||
|
valid_recipe_one.name: valid_recipe_two
|
||||||
|
}
|
||||||
|
with self.assertRaises(KeyError):
|
||||||
|
create_rules({}, recipes)
|
@ -76,3 +76,4 @@ class CorrectnessTests(unittest.TestCase):
|
|||||||
fejnr = FileEventJupyterNotebookRule("name", fep, jnr)
|
fejnr = FileEventJupyterNotebookRule("name", fep, jnr)
|
||||||
|
|
||||||
self.assertEqual(fejnr.recipe, jnr)
|
self.assertEqual(fejnr.recipe, jnr)
|
||||||
|
|
||||||
|
@ -3,7 +3,8 @@ import unittest
|
|||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from core.correctness.validation import check_input, valid_string, valid_dict
|
from core.correctness.validation import check_input, valid_string, \
|
||||||
|
valid_dict, valid_list
|
||||||
from core.correctness.vars import VALID_NAME_CHARS
|
from core.correctness.vars import VALID_NAME_CHARS
|
||||||
|
|
||||||
|
|
||||||
@ -85,3 +86,23 @@ class CorrectnessTests(unittest.TestCase):
|
|||||||
def testValidDictOverlyStrict(self)->None:
|
def testValidDictOverlyStrict(self)->None:
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
valid_dict({"a": 0, "b": 1}, str, int)
|
valid_dict({"a": 0, "b": 1}, str, int)
|
||||||
|
|
||||||
|
def testValidListMinimum(self)->None:
|
||||||
|
valid_list([1, 2, 3], int)
|
||||||
|
valid_list(["1", "2", "3"], str)
|
||||||
|
valid_list([1], int)
|
||||||
|
|
||||||
|
def testValidListAltTypes(self)->None:
|
||||||
|
valid_list([1, "2", 3], int, alt_types=[str])
|
||||||
|
|
||||||
|
def testValidListMismatchedNotList(self)->None:
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
valid_list((1, 2, 3), int)
|
||||||
|
|
||||||
|
def testValidListMismatchedType(self)->None:
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
valid_list([1, 2, 3], str)
|
||||||
|
|
||||||
|
def testValidListMinLength(self)->None:
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
valid_list([1, 2, 3], str, min_length=10)
|
||||||
|
Reference in New Issue
Block a user