From 4c908355c27f4c254907cf9a1a259747b6ea317a Mon Sep 17 00:00:00 2001 From: Gabriel Lanata Date: Fri, 3 Apr 2020 14:11:32 -0700 Subject: [PATCH] Add Enforce Runtime Attributes rule (#56) --- README.md | 12 +++-- xiblint/__init__.py | 2 +- xiblint/rules/enforce_runtime_attributes.py | 54 +++++++++++++++++++++ xiblint/rules/enforce_system_properties.py | 2 +- 4 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 xiblint/rules/enforce_runtime_attributes.py diff --git a/README.md b/README.md index 8410ead..22cd5ea 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,14 @@ in the .xib or .storyboard file. Checks for labels with outlets into a view controller that have no accessibility identifiers. Labels with outlets might get dynamic text, and therefore should be accessible to UI testing. +- `enforce_runtime_attributes` + + Ensures a runtime attribute is set to one of the allowed values. Configure `runtime_attributes` in a custom rule configuration using `rules_config` (see below). Use `null` as an option to allow no value. + +- `enforce_system_properties` + + Ensures a property in a system type is set to one of the allowed values. Configure `system_properties` in a custom rule configuration using `rules_config` (see below). Use `null` as an option to allow default value. + - `named_colors` Ensures all colors are using named colors from an asset catalog. Configure `ignore_alpha` (default is `false`) in a custom rule configuration using `rules_config` (see below) if you’d like to ignore colors with alpha. @@ -76,10 +84,6 @@ in the .xib or .storyboard file. Ensures a system type uses a set of custom clases. Configure `system_classes` in a custom rule configuration using `rules_config` (see below). -- `enforce_system_properties` - - Ensures a property in a system type is set to one of the allowed properties. Configure `system_properties` in a custom rule configuration using `rules_config` (see below). Use `null` as an option to allow default value. - ## Usage For a list of available rules, run `xiblint -h`. diff --git a/xiblint/__init__.py b/xiblint/__init__.py index a86e069..ba0128c 100644 --- a/xiblint/__init__.py +++ b/xiblint/__init__.py @@ -1 +1 @@ -__version__ = '0.9.13' +__version__ = '0.9.14' diff --git a/xiblint/rules/enforce_runtime_attributes.py b/xiblint/rules/enforce_runtime_attributes.py new file mode 100644 index 0000000..12c67ec --- /dev/null +++ b/xiblint/rules/enforce_runtime_attributes.py @@ -0,0 +1,54 @@ +from xiblint.rules import Rule +from xiblint.xibcontext import XibContext + + +class EnforceRuntimeAttributes(Rule): + """ + Ensures a runtime attribute is set to one of the allowed values. + + You must specify a module as part of the class name if it is a custom type. + + Example configuration: + { + "runtime_attributes": { + "SomeModule.LegacyButton": { + "sizeName": ["small", "large"] + }, + "button": { + "layer.cornerRadius": [null] + } + } + } + """ + def check(self, context): # type: (XibContext) -> None + runtime_attributes = self.config.get('runtime_attributes', {}) + + for full_class_name in runtime_attributes.keys(): + enforced_attributes = runtime_attributes.get(full_class_name) + + # Get system or custom class elements + full_class_name_split = full_class_name.split(".") + if len(full_class_name_split) == 1: + elements = context.tree.findall(".//{}".format(full_class_name)) + elif len(full_class_name_split) == 2: + elements = context.tree.findall(".//*[@customClass='{}'][@customModule='{}']" + .format(full_class_name_split[1], full_class_name_split[0])) + + for element in elements: + for attribute_keyPath in enforced_attributes.keys(): + attribute_allowed_values = enforced_attributes.get(attribute_keyPath) + attribute_value = None + + attribute_list = element.find("./userDefinedRuntimeAttributes") + if attribute_list is not None: + attribute_element = attribute_list.find("./userDefinedRuntimeAttribute/[@keyPath='{}']" + .format(attribute_keyPath)) + if attribute_element is not None: + attribute_value = attribute_element.get("value") + + if attribute_value in attribute_allowed_values: + continue + + options_string = '`, `'.join(map(str, attribute_allowed_values)) + context.error(element, '`<{}>` runtime attribute `{}` must use `{}` instead of `{}`.' + .format(full_class_name, attribute_keyPath, options_string, attribute_value)) diff --git a/xiblint/rules/enforce_system_properties.py b/xiblint/rules/enforce_system_properties.py index 877bc69..edf2adf 100644 --- a/xiblint/rules/enforce_system_properties.py +++ b/xiblint/rules/enforce_system_properties.py @@ -4,7 +4,7 @@ class EnforceSystemProperties(Rule): """ - Ensures unavailable system properties are not used. + Ensures a property in a system type is set to one of the allowed values. Example configuration: {