def test_method_name_same_on_class_and_instance(self): # Check this works with Python object class. class MyClass: def value_changed(self): pass a = MyClass() self.assertEqual(get_method_name(a.value_changed), get_method_name(MyClass.value_changed)) # Check this works with Aggregate class and @event decorator. class MyAggregate(Aggregate): @event def value_changed(self): pass a = MyAggregate() self.assertEqual(get_method_name(a.value_changed), get_method_name(MyAggregate.value_changed)) self.assertTrue( get_method_name(a.value_changed).endswith("value_changed"), ) self.assertTrue( get_method_name( MyAggregate.value_changed).endswith("value_changed"), ) # Check this works with Aggregate class and @event decorator. class MyAggregate2(Aggregate): @event() def value_changed(self): pass a = MyAggregate2() self.assertEqual( get_method_name(a.value_changed), get_method_name(MyAggregate2.value_changed), ) self.assertTrue( get_method_name(a.value_changed).endswith("value_changed"), ) self.assertTrue( get_method_name( MyAggregate2.value_changed).endswith("value_changed"), )
def test_aggregate_has_incompatible_created_event_class_in_event_decorator( self): class MyAggregate1(Aggregate): class Started(AggregateCreated): a: int @event(Started) def __init__(self): pass with self.assertRaises(TypeError) as cm: MyAggregate1() self.assertTrue(cm.exception.args[0].startswith( "Unable to construct 'Started' event")) with self.assertRaises(TypeError) as cm: MyAggregate1(a=1) method_name = get_method_name(MyAggregate1.__init__) self.assertEqual( f"{method_name}() got an unexpected keyword argument 'a'", cm.exception.args[0], ) class MyAggregate2(Aggregate): class Started(AggregateCreated): pass @event(Started) def __init__(self, a: int): self.a = a with self.assertRaises(TypeError) as cm: MyAggregate2() method_name = get_method_name(MyAggregate2.__init__) self.assertEqual( f"{method_name}() missing 1 required positional argument: 'a'", cm.exception.args[0], ) with self.assertRaises(TypeError) as cm: MyAggregate2(a=1) self.assertTrue( cm.exception.args[0].startswith( "Unable to construct 'Started' event:"), cm.exception.args[0], )
def test_init_defined_as_dataclass_with_default(self): class MyAgg(Aggregate): value: int = 0 a = MyAgg(1) self.assertIsInstance(a, MyAgg) self.assertEqual(a.value, 1) self.assertIsInstance(a, Aggregate) self.assertEqual(len(a.pending_events), 1) a = MyAgg(value=1) self.assertIsInstance(a, MyAgg) self.assertEqual(a.value, 1) self.assertIsInstance(a, Aggregate) self.assertEqual(len(a.pending_events), 1) a = MyAgg() self.assertIsInstance(a, MyAgg) self.assertEqual(a.value, 0) self.assertIsInstance(a, Aggregate) self.assertEqual(len(a.pending_events), 1) with self.assertRaises(TypeError) as cm: MyAgg(wrong=1) method_name = get_method_name(MyAgg.__init__) self.assertEqual( f"{method_name}() got an unexpected keyword argument 'wrong'", cm.exception.args[0], )
def test_raises_when_init_gets_unexpected_keyword_argument(self): class MyAgg(Aggregate): def __init__(self, a=1): pass with self.assertRaises(TypeError) as cm: MyAgg(b=1) method_name = get_method_name(MyAgg.__init__) self.assertEqual( cm.exception.args[0], f"{method_name}() got an unexpected keyword argument 'b'", ) with self.assertRaises(TypeError) as cm: MyAgg(c=1) self.assertEqual( cm.exception.args[0], f"{method_name}() got an unexpected keyword argument 'c'", ) with self.assertRaises(TypeError) as cm: MyAgg(b=1, c=1) self.assertEqual( cm.exception.args[0], f"{method_name}() got an unexpected keyword argument 'b'", )
def test_raises_when_init_missing_required_positional_and_keyword_only_arg( self): class MyAgg(Aggregate): def __init__(self, a, *, b): pass with self.assertRaises(TypeError) as cm: MyAgg() method_name = get_method_name(MyAgg.__init__) self.assertEqual( cm.exception.args[0], f"{method_name}() missing 1 required positional argument: 'a'", ) class MyAgg(Aggregate): def __init__(self, a, b=0, *, c): self.a = a self.b = b self.c = c with self.assertRaises(TypeError) as cm: MyAgg(c=2) self.assertEqual( cm.exception.args[0], f"{method_name}() missing 1 required positional argument: 'a'", )
def assert_raises(cls): obj = cls() with self.assertRaises(TypeError) as cm: obj.value_changed() name = get_method_name(obj.value_changed) self.assertEqual( f"{name}() missing 2 required positional arguments: 'a' and 'b'", cm.exception.args[0], )
def assert_raises(cls): obj = cls() with self.assertRaises(TypeError) as cm: # noinspection PyArgumentList obj.value_changed(1, 2) name = get_method_name(cls.value_changed) self.assertEqual( f"{name}() takes 2 positional arguments but 3 were given", cm.exception.args[0], )
def assert_raises(cls): obj = cls() with self.assertRaises(TypeError) as cm: obj.value_changed(b=1) name = get_method_name(cls.value_changed) self.assertEqual( f"{name}() got an unexpected keyword argument 'b'", cm.exception.args[0], )
def assert_raises(cls): obj = cls() with self.assertRaises(TypeError) as cm: obj.value_changed(1) name = get_method_name(cls.value_changed) self.assertEqual( f"{name}() missing 2 required keyword-only arguments: 'b' and 'c'", cm.exception.args[0], )
def test_raises_when_init_missing_2_required_positional_args(self): class MyAgg(Aggregate): def __init__(self, a, b, *, c): pass with self.assertRaises(TypeError) as cm: MyAgg() method_name = get_method_name(MyAgg.__init__) self.assertEqual( cm.exception.args[0], f"{method_name}() missing 2 required positional arguments: 'a' and 'b'", )
def test_raises_when_create_args_mismatch_created_event(self): class BrokenAggregate(Aggregate): @classmethod def create(cls, name): return cls._create(event_class=cls.Created, id=uuid4(), name=name) with self.assertRaises(TypeError) as cm: BrokenAggregate.create("name") method_name = get_method_name(BrokenAggregate.Created.__init__) self.assertEqual( (f"Unable to construct 'Created' event: " f"{method_name}() got an unexpected keyword argument 'name'"), cm.exception.args[0], )
def assert_raises(cls): method_name = get_method_name(cls.__init__) with self.assertRaises(TypeError) as cm: cls(0) self.assertEqual( cm.exception.args[0], f"{method_name}() takes 1 positional argument but 2 were given", ) with self.assertRaises(TypeError) as cm: cls(value=0) self.assertEqual( cm.exception.args[0], f"{method_name}() got an unexpected keyword argument 'value'", )
def test_init(cls): obj = cls(b=1, a=2) self.assertEqual(obj.a, 2) self.assertEqual(obj.b, 1) self.assertEqual(obj.c, 1) self.assertEqual(obj.d, 2) obj = cls(1, 2, 3, 4) self.assertEqual(obj.a, 1) self.assertEqual(obj.b, 2) self.assertEqual(obj.c, 3) self.assertEqual(obj.d, 4) with self.assertRaises(TypeError) as cm: obj = cls(1, 2, 3, c=4) self.assertEqual(obj.a, 1) self.assertEqual(obj.b, 2) self.assertEqual(obj.c, 4) self.assertEqual(obj.d, 3) method_name = get_method_name(cls.__init__) self.assertEqual( f"{method_name}() got multiple values for argument 'c'", cm.exception.args[0], ) with self.assertRaises(TypeError) as cm: obj = cls(1, a=2, d=3, c=4) self.assertEqual(obj.a, 2) self.assertEqual(obj.b, 1) self.assertEqual(obj.c, 4) self.assertEqual(obj.d, 3) self.assertEqual( f"{method_name}() got multiple values for argument 'a'", cm.exception.args[0], )
def _spec_coerce_args_to_kwargs( method: Union[FunctionType, WrapperDescriptorType], len_args: int, kwargs_keys: Tuple[str], expects_id: bool, ) -> Tuple[Tuple[Tuple[int, str], ...], Tuple[Tuple[str, Any], ...]]: method_signature = inspect.signature(method) positional_names = [] keyword_defaults = {} required_positional = [] required_keyword_only = [] if expects_id: positional_names.append("id") required_positional.append("id") for name, param in method_signature.parameters.items(): if name == "self": continue # elif param.kind in (param.POSITIONAL_ONLY, param.POSITIONAL_OR_KEYWORD): if param.kind is param.KEYWORD_ONLY: required_keyword_only.append(name) if param.kind is param.POSITIONAL_OR_KEYWORD: positional_names.append(name) if param.default == param.empty: required_positional.append(name) if param.default != param.empty: keyword_defaults[name] = param.default # if not required_keyword_only and not positional_names: # if args or kwargs: # raise TypeError(f"{method.__name__}() takes no args") method_name = get_method_name(method) for name in kwargs_keys: if name not in required_keyword_only and name not in positional_names: raise TypeError( f"{method_name}() got an unexpected " f"keyword argument '{name}'" ) if len_args > len(positional_names): msg = ( f"{method_name}() takes {len(positional_names) + 1} " f"positional argument{'' if len(positional_names) + 1 == 1 else 's'} " f"but {len_args + 1} were given" ) raise TypeError(msg) required_positional_not_in_kwargs = [ n for n in required_positional if n not in kwargs_keys ] num_missing = len(required_positional_not_in_kwargs) - len_args if num_missing > 0: missing_names = [ f"'{name}'" for name in required_positional_not_in_kwargs[len_args:] ] msg = ( f"{method_name}() missing {num_missing} required positional " f"argument{'' if num_missing == 1 else 's'}: " ) raise_missing_names_type_error(missing_names, msg) counter = 0 args_names = [] for name in positional_names: if counter + 1 > len_args: break if name in kwargs_keys: raise TypeError( f"{method_name}() got multiple values for argument '{name}'" ) else: args_names.append(name) counter += 1 missing_keyword_only_arguments = [] for name in required_keyword_only: if name not in kwargs_keys: missing_keyword_only_arguments.append(name) if missing_keyword_only_arguments: missing_names = [f"'{name}'" for name in missing_keyword_only_arguments] msg = ( f"{method_name}() missing {len(missing_names)} " f"required keyword-only argument" f"{'' if len(missing_names) == 1 else 's'}: " ) raise_missing_names_type_error(missing_names, msg) for key in tuple(keyword_defaults.keys()): if key in args_names or key in kwargs_keys: keyword_defaults.pop(key) enumerated_args_names = tuple(enumerate(args_names)) keyword_defaults_items = tuple(keyword_defaults.items()) return enumerated_args_names, keyword_defaults_items