def _valid_methods(self, value): if not isinstance(value, dict): if not isinstance(value, yaml_loader.YamlNull): yield error.report.E046(_('Methods are not a dict'), value) return for method_name, method_data in value.items(): if not isinstance(method_data, dict): if method_data: yield error.report.E046(_('Method is not a dict'), method_name) return if not (method_name in SPECIAL_METHODS or METHOD_NAME_REGEX.match(method_name)): yield error.report.E054( _('Invalid name of method "{}"').format(method_name), method_name) scope = method_data.get('Scope') if scope: yield self._valid_scope(scope) usage = method_data.get('Usage') if usage: yield self._valid_method_usage(usage) arguments = method_data.get('Arguments') if arguments: yield self._valid_arguments(arguments) body = method_data.get('Body') if body: yield self._valid_body(body) yield self._valid_keywords(method_data.keys(), METHOD_KEYWORDS)
def _valid_arguments(self, arguments): if isinstance(arguments, dict) and len(arguments) > 1: yield error.report.E048( _('It is not safe to define methods ' 'arguments as a dict with several keys'), arguments) return elif not isinstance(arguments, (list, dict)): yield error.report.E046( _('Methods arguments should be a list or ' 'dict'), arguments) return if isinstance(arguments, dict): arguments = [arguments] for argument in arguments: if not isinstance(argument, dict) or len(argument) != 1: yield error.report.E046( _('Methods single argument should be ' 'a one key dict'), argument) else: name = next(six.iterkeys(argument)) if not self._check_name(name): yield error.report.E054( _('Invalid name of argument "{}"').format(name), name) val = next(six.itervalues(argument)) contract = val.get('Contract') if contract: yield self._valid_contract(contract) usage = val.get('Usage') if usage: yield self._valid_argument_usage(usage) yield self._valid_keywords(val, METHOD_ARGUMENTS_KEYWORDS)
def _valid_contract(self, contract): if isinstance(contract, list): if len(contract) > 1: if len(contract) < 3: if isinstance(contract[1], int): return elif len(contract) < 4: if isinstance(contract[1], int) and \ isinstance(contract[2], int): return for con in contract: yield self._valid_contract(con) elif len(contract) == 1: yield self._valid_contract(contract[0]) elif isinstance(contract, dict): if not contract: return for c_key, c_value in contract.items(): yield self._valid_string(c_key) yield self._valid_contract(c_value) elif isinstance(contract, six.string_types): if not self.yaql_checker(contract) or \ not contract.startswith('$.') and contract != '$': yield error.report.W048( _('Contract is not valid yaql "{}"' '').format(contract), contract) else: yield error.report.W048( _('Contract is not valid yaql "{}"' '').format(contract), contract)
def _valid_require(self, value): if not isinstance(value, dict): yield error.report.E005(_('Require is not a dict type'), value) return for fqn, ver in value.items(): if not self._check_fqn_name(fqn): yield error.report.E005(_('Require key is not valid FQN "{}"' '').format(fqn), fqn)
def _valid_argument_usage(self, usage): if self._loaded_pkg.format_version < '1.4': yield error.report.E052( _('Arguments usage is available ' 'since 1.4'), usage) if usage not in frozenset(['Standard', 'VarArgs', 'KwArgs']): yield error.report.E053( _('Usage is invalid value "{}"').format(usage), usage)
def _valid_scope(self, scope): if self._loaded_pkg.format_version >= '1.4': if scope is not None and scope not in ('Public', 'Session'): yield error.report.E044( _('Wrong Scope "{}"').format(scope), scope) else: yield error.report.E044( _('Scope is not supported version ' 'earlier than 1.3"'), scope)
def _valid_application(self, application): if not isinstance(application, dict): yield error.report.E084(_('Application is not a dict'), application) return for name, value in application.items(): if not self._check_name(name): if name != '?': yield error.report.E083(_('Wrong name in UI file "{}"') .format(name), name)
def _valid_ui(self, value): if isinstance(value, six.string_types): pkg_type = self._loaded_pkg.read( consts.MANIFEST_PATH).yaml()[0]['Type'] if pkg_type == 'Library': return if not self._loaded_pkg.exists(os.path.join('UI', value)): yield error.report.W073(_('There is no UI file "{}"' '').format(value), value) else: yield error.report.E072(_('UI is not a string'), value)
def _valid_format(self, value): format_ = str(value).split('/', 1) if len(format_) > 1: if format_[0] != 'MuranoPL': yield error.report.W030(_('Not supported format "{}"' '').format(value), value) return ver = format_[-1] if str(ver) not in ['1.0', '1.1', '1.2', '1.3', '1.4']: yield error.report.W030(_('Not supported format version "{}"' '').format(value), value)
def _valid_logo(self, value): if isinstance(value, six.string_types): pkg_type = self._loaded_pkg.read( consts.MANIFEST_PATH).yaml()[0]['Type'] if pkg_type == 'Library': return if not self._loaded_pkg.exists(value): yield error.report.W074(_('There is no Logo file "{}"' '').format(value), value) else: yield error.report.E074(_('Logo is not a string'), value)
def _known_directories(self): files = set(self._loaded_pkg.search_for('^[^/]+$')) try: logo_file = next(self._loaded_pkg.search_for('^manifest.yaml$'))\ .yaml()[0]['Logo'] except Exception: logo_file = 'logo.png' for file_ in files - KNOWN_FILES_DIR - {logo_file}: yield error.report.W120( _('Unknown "{}" in the package').format(file_), file_) for file_ in REQUIRED_FILES_DIR - files: yield error.report.W121( _('Missing "{}" in the package').format(file_), file_)
def _valid_method_usage(self, usage): if usage == 'Action': if self._loaded_pkg.format_version >= '1.4': yield error.report.W045( _('Usage "{}" is deprecated since 1.4' '').format(usage), usage) elif usage in frozenset(['Static', 'Extension']): if self._loaded_pkg.format_version <= '1.3': yield error.report.W045( _('Usage "{}" is available from 1.3').format(usage), usage) elif usage != 'Runtime': yield error.report.W045( _('Unsupported usage type "{}" ').format(usage), usage)
def _valid_name(self, value): if not isinstance(value, six.string_types): yield error.report.E011( _('Invalid class name "{}". ' 'Class name should be a string').format(value), value) elif (value.startswith('__') or not CLASSNAME_REGEX.match(value)): yield error.report.E011( _('Invalid class name "{}"').format(value), value) else: # NOTE (kzaitsev): allow short uppercase names like Q, IP, CDN if (not value[0].isupper() or (len(value) > 3 and value == value.upper())): yield error.report.W011( _('Class name "{}" not in CamelCase').format(value), value)
def _valid_namespaces(self, value): if not isinstance(value, dict): yield error.report.E044(_('Wrong type of namespace'), value) return for name, fqn in value.items(): if not self._check_fqn_name(fqn): yield error.report.W060( _('Wrong namespace fqn ' '"{}"').format(fqn), fqn) if not self._check_name(name) and name != '=': yield error.report.E060( _('Wrong name for namespace ' '"{}"').format(fqn), fqn)
def _valid_version(self, version): try: semantic_version.Version.coerce(str(version)) except ValueError: yield error.report.E071(_('Version format should be compatible ' 'with SemVer not "{}"' '').format(version), version)
def _valid_body(self, body): if not isinstance(body, (list, six.string_types, dict)): yield error.report.E045( _('Body is not a list or scalar/yaql ' 'expression'), body) else: yield self.code_structure.codeblock(body)
def _valid_classes(self, value): if not isinstance(value, dict): yield error.report.E074(_('Classes section should be a dict'), value) return files = set(value.values()) existing_files = set(self._loaded_pkg.search_for('.*', 'Classes')) for fname in files - existing_files: yield error.report.E050(_('File "{}" is present in Manifest, ' 'but not in filesystem' '').format(fname), fname) for fname in existing_files - files: yield error.report.W020(_('File "{}" is not present in Manifest, ' 'but it is in filesystem' '').format(fname), fname)
def _valid_import(self, import_, can_be_list=True): if can_be_list and isinstance(import_, list): for imp in import_: yield self._valid_import(imp, False) elif not self._check_ns_fqn_name(import_): yield error.report.E025( _('Wrong namespace or FNQ of extended ' 'class "{0}"').format(import_), import_)
def _valid_applies(self, applies, allow_list=True): if allow_list and isinstance(applies, list): for apl in applies: yield self._valid_applies(apl, False) else: if not isinstance(applies, six.string_types) or \ applies not in APPLIES_VALUES: yield error.report.E028( _('Wrong Applies "{0}"').format(applies), applies)
def _to_list(self, error_chain, select=None, ignore=None): errors = [] while True: try: e = next(error_chain) except StopIteration: break except Exception: exc_info = sys.exc_info() tb = exc_info[2] while tb.tb_next: tb = tb.tb_next validator_class = tb.tb_frame.f_locals.get('self') check_name = tb.tb_frame.f_code.co_name check_locals = tb.tb_frame.f_locals.copy() check_locals.pop('self', None) if validator_class: msg = (_('Checker {} from {} failed!' '').format(check_name, validator_class.__class__.__name__)) else: msg = (_('Checker {} failed!' '').format(check_name)) LOG.error('{} {}\n{}'.format(msg, _('Checker locals:'), pprint.pformat(check_locals)), exc_info=exc_info) e = error.report.E000( msg + _(' See more information in logs.')) if isinstance(e, types.GeneratorType): errors.extend(self._to_list(e, select, ignore)) else: if ((select and e.code not in select) or (ignore and e.code in ignore)): LOG.debug('Skipped: {code} {message}' ''.format(**e.to_dict())) continue LOG.debug('Reported: {code} {message}' ''.format(**e.to_dict())) errors.append(e) return sorted(errors, key=lambda err: err.code)
def _run_single(self, file_): reports_chain = [] def run_helper(name, checkers, data): for checker in checkers: result = checker(data) if result: reports_chain.append(result) try: multi_documents = file_.yaml() except Exception as e: reports_chain.append([ error.report.E002('Yaml Error: {0}'.format(e), e)]) else: if multi_documents is None: multi_documents = [{}] if len(multi_documents) > 1 and not self._allows_multi: reports_chain.append([ error.report.E005(_('Multi document is not allowed in {}') .format(file_._path))]) for ast in multi_documents: file_check = self._checkers.get(None) if file_check: run_helper(None, file_check['checkers'], ast) for key, value in ast.items(): checkers = self._checkers.get(key) if checkers: run_helper(key, checkers['checkers'], ast[key]) else: reports_chain.append(self._unknown_keyword(key, value)) missing = set(key for key, value in self._checkers.items() if value['required']) - set(ast.keys()) for m in missing: reports_chain.append([ error.report.E020(_('Missing required key "{}"') .format(m), m)]) return itertools.chain(*reports_chain)
def load_package(path, quiet=False): for loader_cls in PACKAGE_LOADERS: loader = loader_cls.try_load(path) if loader is not None: return loader else: if not quiet: LOG.debug("{} failed to load '{}'" "".format(loader_cls.__name__, path)) else: raise ValueError(_('Can not load package: "{}"').format(path))
def _valid_extends(self, value, can_be_list=True): if can_be_list and isinstance(value, list): for cls in value: yield self._valid_extends(cls, False) elif isinstance(value, six.string_types): if not self._check_ns_fqn_name(value): yield error.report.E025( _('Wrong FNQ of extended class "{}"' '').format(value), value) else: yield error.report.E025("Wrong type of Extends field", value)
def _valid_form(self, form): for named_params in form: for key, value in named_params.items(): if key in STR_FIELDS: if not isinstance(value, six.string_types): yield error.report.E040(_('Value of {} should be ' 'string not "{}"') .format(key, value), key) elif key in BOOL_FIELDS: if not isinstance(value, bool): yield error.report.E081(_('Value of {} should be ' 'boolean not "{}"') .format(key, value), key) elif key in INT_FIELDS: if not isinstance(value, int): yield error.report.E082(_('Value of {} should be ' 'int not "{}"') .format(key, value), key) elif key == 'type': yield self._valid_field_type(value)
def _valid_properties(self, properties): if not isinstance(properties, dict): yield error.report.E026(_('Properties should be a dict'), properties) return for property_name, property_data in properties.items(): usage = property_data.get('Usage') if usage: if usage not in PROPERTIES_USAGE_VALUES: yield error.report.E042( _('Not allowed usage "{}"' '').format(usage), usage) contract = property_data.get('Contract') if contract is not None: yield self._valid_contract(contract) else: yield error.report.E047( _('Missing Contract in property "{}"').format( property_name), property_name) yield self._valid_keywords(property_data.keys(), PROPERTIES_KEYWORDS)
def _valid_version(self, version): if str(version) not in UI_VERSION: yield error.report.W082(_('Incorrect version of UI file "{}"') .format(version), version)
def _unknown_keyword(self, key, value): yield error.report.W010(_('Unknown keyword "{}"').format(key), key)
def _valid_string(self, value): if not isinstance(value, six.string_types): yield error.report.E040(_('Value is not a string "{}"' '').format(value), value)
def _valid_inherited(self, inherited): if not isinstance(inherited, bool): yield error.report.E027( _('Inherited is not bool "{0}"').format(inherited), inherited)
def _valid_cardinality(self, cardinality): if cardinality not in ('One', 'Many'): yield error.report.E027( _('Wrong Cardinality "{0}"').format(cardinality), cardinality)