def test_attrs_arrays(Layer, data, ndim): """Test layer attributes and arrays.""" np.random.seed(0) layer = Layer(data) # Check layer has been correctly created assert layer.ndim == ndim properties = layer._get_state() # Check every property is in call signature signature = callsignature(Layer) for prop in properties.keys(): assert prop in signature.parameters # Check number of properties is same as number in signature assert len(properties) == len(signature.parameters) # Check new layer can be created new_layer = Layer(**properties) # Check that new layer matches old on all properties: for prop in properties.keys(): # If lists check equality of all elements with np.all if isinstance(getattr(layer, prop), list): assert np.all([ np.all(ol == nl) for ol, nl in zip(getattr(layer, prop), getattr(new_layer, prop)) ]) elif isinstance(getattr(layer, prop), dict): assert np.all([ np.all(value == getattr(new_layer, prop)[key]) for key, value in getattr(layer, prop).items() ]) else: assert np.all(getattr(layer, prop) == getattr(new_layer, prop))
def test_signature(layer): name = layer.__name__ method = getattr(Viewer, f'add_{camel_to_snake(name)}') class_parameters = dict(inspect.signature(layer.__init__).parameters) method_parameters = dict(inspect.signature(method).parameters) # Remove path and data parameters from viewer method if path exists if 'path' in method_parameters: del method_parameters['path'] del method_parameters['data'] del class_parameters['data'] fail_msg = f"signatures don't match for class {name}" if name == 'Image': # If Image just test that class params appear in method for class_param in class_parameters.keys(): assert class_param in method_parameters.keys(), fail_msg else: assert class_parameters == method_parameters, fail_msg code = inspect.getsource(method) # Below, we test that somewhere in the source code of the method, a call to # the corresponding Layer.__init__ method is made that has all of the same # parameters. add_image has a special implementation, and therefore # requires a modified test. if name == 'Image': # it becomes very cumbersome to have to type out all of the # parameters in add_image for both single images, and all the iterables # when channel_axis is supplied, so the approach was changed in # https://github.com/napari/napari/pull/1092 # this makes sure we're still passing all the proper arguments args = re.search(r'kwargs = \{(.+?)\}', code, flags=re.S) args = ' '.join(args.group(1).split()) # convert 'arg': arg -> arg=arg args = 'data, ' + re.sub(r"['\"]([^'\"]+)['\"]:\s?", '\\1=', args) else: args = re.search(rf'layer = layers\.{name}\((.+?)\)', code, flags=re.S) # get the arguments & normalize whitepsace args = ' '.join(args.group(1).split()) if args.endswith(','): # remove tailing comma if present args = args[:-1] autogen = callsignature(layer) autogen = autogen.replace( # remove 'self' parameter parameters=[p for k, p in autogen.parameters.items() if k != 'self']) autogen = str(autogen)[1:-1] # remove parentheses try: assert args == autogen except AssertionError as e: msg = ('arguments improperly passed from convenience ' f'method to layer {name}') raise SyntaxError(msg) from e
def test_signature(layer): name = layer.__name__ method = getattr(Viewer, f'add_{camel_to_snake(name)}') class_parameters = dict(inspect.signature(layer.__init__).parameters) method_parameters = dict(inspect.signature(method).parameters) # Remove path and data parameters from viewer method if path exists if 'path' in method_parameters: del method_parameters['path'] del method_parameters['data'] del class_parameters['data'] fail_msg = f"signatures don't match for class {name}" if name == 'Image': # If Image just test that class params appear in method for class_param in class_parameters.keys(): assert class_param in method_parameters.keys(), fail_msg else: assert class_parameters == method_parameters, fail_msg code = inspect.getsource(method) args = re.search(rf'layer = layers\.{name}\((.+?)\)', code, flags=re.S) # get the arguments & normalize whitepsace args = ' '.join(args.group(1).split()) if args.endswith(','): # remove tailing comma if present args = args[:-1] autogen = callsignature(layer) autogen = autogen.replace( # remove 'self' parameter parameters=[p for k, p in autogen.parameters.items() if k != 'self']) autogen = str(autogen)[1:-1] # remove parentheses try: assert args == autogen except AssertionError as e: msg = f'arguments improperly passed from convenience method to layer {name}' # noqa: E501 raise SyntaxError(msg) from e
def test_callsignature(): # no arguments assert str(callsignature(lambda: None)) == '()' # one arg assert str(callsignature(lambda a: None)) == '(a)' # multiple args assert str(callsignature(lambda a, b: None)) == '(a, b)' # arbitrary args assert str(callsignature(lambda *args: None)) == '(*args)' # arg + arbitrary args assert str(callsignature(lambda a, *az: None)) == '(a, *az)' # default arg assert str(callsignature(lambda a=42: None)) == '(a=a)' # multiple default args assert str(callsignature(lambda a=0, b=1: None)) == '(a=a, b=b)' # arg + default arg assert str(callsignature(lambda a, b=42: None)) == '(a, b=b)' # arbitrary kwargs assert str(callsignature(lambda **kwargs: None)) == '(**kwargs)' # default arg + arbitrary kwargs assert str(callsignature(lambda a=42, **kwargs: None)) == '(a=a, **kwargs)' # arg + default arg + arbitrary kwargs assert str(callsignature(lambda a, b=42, **kw: None)) == '(a, b=b, **kw)' # arbitary args + arbitrary kwargs assert str(callsignature(lambda *args, **kw: None)) == '(*args, **kw)' # arg + default arg + arbitrary kwargs assert (str(callsignature(lambda a, b=42, *args, **kwargs: None)) == '(a, b=b, *args, **kwargs)') # kwonly arg assert str(callsignature(lambda *, a: None)) == '(a=a)' # arg + kwonly arg assert str(callsignature(lambda a, *, b: None)) == '(a, b=b)' # default arg + kwonly arg assert str(callsignature(lambda a=42, *, b: None)) == '(a=a, b=b)' # kwonly args + everything assert (str(callsignature(lambda a, b=42, *, c, d=5, **kwargs: None)) == '(a, b=b, c=c, d=d, **kwargs)')