def generate_strategies( table: generate_property_table.Table) -> Dict[str, SymbolicStrategy]: # variable identifier -> strategy how to generate the identifier strategies: Dict[str, SymbolicStrategy] = dict() for row in table.get_rows(): if row.kind == generate_property_table.Kind.BASE: strategies[row.var_id] = _infer_strategy(row, table) return strategies
def _infer_int_strategy( row: generate_property_table.Row, table: generate_property_table.Table) -> SymbolicIntegerStrategy: max_value: List[Property] = [] min_value: List[Property] = [] filters: List[Lambda] = [] min_value_contains_free_variables = False max_value_contains_free_variables = False for property_identifier, row_property in row.properties.items(): if property_identifier == '<': row_property_decremented = _decrement_property(row_property) row_property_decremented.identifier = '<=' max_value.append(row_property_decremented) if row_property.free_variables(): max_value_contains_free_variables = True elif property_identifier == '<=': max_value.append(row_property) if row_property.free_variables(): max_value_contains_free_variables = True elif property_identifier == '>': row_property_incremented = _increment_property(row_property) row_property_incremented.identifier = '>=' min_value.append(row_property_incremented) if row_property.free_variables(): min_value_contains_free_variables = True elif property_identifier == '>=': min_value.append(row_property) if row_property.free_variables(): min_value_contains_free_variables = True else: filters.extend(property_to_lambdas(row_property)) # if min_value_contains_free_variables or max_value_contains_free_variables: # # turn min values into filters # for prop in min_value: # filters.extend(property_to_lambdas(prop)) # min_value = [] min_value_deserialized: List[str] = list( chain(*[represent_property_arguments(prop) for prop in min_value])) max_value_deserialized: List[str] = list( chain(*[represent_property_arguments(prop) for prop in max_value])) for link_row in table.get_rows(): if link_row.parent == row.var_id and link_row.kind == generate_property_table.Kind.LINK: link_strategy = _infer_int_strategy(link_row, table) min_value_deserialized.extend(link_strategy.min_value) max_value_deserialized.extend(link_strategy.max_value) filters.extend(link_strategy.filters) return SymbolicIntegerStrategy(var_id=row.var_id, min_value=min_value_deserialized, max_value=max_value_deserialized, filters=filters)
def _infer_set_strategy( row: generate_property_table.Row, table: generate_property_table.Table) -> SymbolicSetStrategy: elements: Optional[SymbolicStrategy] = None min_size: List[str] = [] max_size: List[str] = [] filters: List[Lambda] = [] for other_row in table.get_rows(): if other_row.parent == row.var_id: if other_row.kind == generate_property_table.Kind.LINK: link_property = other_row.var_id[:-(len(other_row.parent) + 2)] for property_identifier, row_property in other_row.properties.items( ): if link_property == 'len': if property_identifier == '<': row_property_decremented = _decrement_property( row_property) max_size.extend( represent_property_arguments( row_property_decremented)) elif property_identifier == '<=': max_size.extend( represent_property_arguments(row_property)) elif property_identifier == '>': row_property_increment = _increment_property( row_property) min_size.extend( represent_property_arguments( row_property_increment)) elif property_identifier == '>=': min_size.extend( represent_property_arguments(row_property)) else: filters.extend(property_to_lambdas(row_property)) else: filters.extend(property_to_lambdas(row_property)) elif other_row.kind == generate_property_table.Kind.UNIVERSAL_QUANTIFIER: elements = _infer_strategy(other_row, table) else: raise NotImplementedError if not elements: elements_type = typing.get_args(row.type)[0] elements = _strategy_from_type_hint(elements_type) return SymbolicSetStrategy(var_id=row.var_id, elements=elements, min_size=min_size, max_size=max_size, filters=filters)
def _infer_from_type_strategy(row: generate_property_table.Row, table: generate_property_table.Table) \ -> SymbolicFromTypeStrategy: filters: List[Lambda] = [] filters.extend([ lambda_property for row_property in row.properties.values() for lambda_property in property_to_lambdas(row_property) ]) for other_row in table.get_rows(): if typing.get_origin(row.type) in [dict, collections.abc.Mapping] and \ other_row.kind == generate_property_table.Kind.UNIVERSAL_QUANTIFIER: if row.var_id in { e for s in other_row.get_dependencies().values() for e in s }: for row_property in other_row.properties.values(): lambdas = property_to_lambdas(row_property) for l in lambdas: if row.var_id in l.free_variables and other_row.var_id in l.free_variables: l.free_variables.remove(other_row.var_id) l.condition = f"all({l.condition} for {other_row.var_id} in {other_row.parent})" filters.extend(lambdas) else: elements = _infer_strategy(other_row, table) filters_to_delete = [] for f in elements.filters: if row.var_id in f.free_variables: new_filter = Lambda( condition= f"all({f.condition} for {other_row.var_id} in {other_row.parent})", free_variables=[row.var_id]) filters.append(new_filter) filters_to_delete.append(f) elements.filters = [ f for f in elements.filters if f not in filters_to_delete ] elif other_row.parent == row.var_id: if other_row.kind == generate_property_table.Kind.LINK: filters.extend([ lambda_property for row_property in other_row.properties.values() for lambda_property in property_to_lambdas(row_property) ]) elif other_row.kind == generate_property_table.Kind.UNIVERSAL_QUANTIFIER: if row.var_id in { e for s in other_row.get_dependencies().values() for e in s }: for row_property in other_row.properties.values(): lambdas = property_to_lambdas(row_property) for l in lambdas: if row.var_id in l.free_variables and other_row.var_id in l.free_variables: l.free_variables.remove(other_row.var_id) l.condition = f"all({l.condition} for {other_row.var_id} in {row.var_id})" filters.extend(lambdas) else: elements = _infer_strategy(other_row, table) filters_to_delete = [] for f in elements.filters: if row.var_id in f.free_variables: new_filter = Lambda( condition= f"all({f.condition} for {other_row.var_id} in {row.var_id})", free_variables=[row.var_id]) filters.append(new_filter) filters_to_delete.append(f) elements.filters = [ f for f in elements.filters if f not in filters_to_delete ] else: raise NotImplementedError # Assume that the other variables are local variables in the strategy for f in filters: f.free_variables = [row.var_id] return SymbolicFromTypeStrategy(var_id=row.var_id, type=row.type, filters=filters)
def _infer_dictionary_strategy( row: generate_property_table.Row, table: generate_property_table.Table) -> SymbolicDictionaryStrategy: keys: Optional[SymbolicStrategy] = None values: Optional[SymbolicStrategy] = None min_size: List[str] = [] max_size: List[str] = [] filters: List[Lambda] = [] for other_row in table.get_rows(): if row.var_id == other_row.var_id: pass elif other_row.parent == row.var_id: if other_row.kind == generate_property_table.Kind.LINK: link_property = other_row.var_id[:-(len(other_row.parent) + 2)] for property_identifier, row_property in other_row.properties.items( ): parents = [] parent = other_row.parent while parent: parents.append(parent) parent = table.get_row_by_var_id(parent).parent if any(parent in row_property.free_variables() for parent in parents): filters.extend(property_to_lambdas(row_property)) elif link_property == 'len': if property_identifier == '<': row_property_decremented = _decrement_property( row_property) max_size.extend( represent_property_arguments( row_property_decremented)) elif property_identifier == '<=': max_size.extend( represent_property_arguments(row_property)) elif property_identifier == '>': row_property_increment = _increment_property( row_property) min_size.extend( represent_property_arguments( row_property_increment)) elif property_identifier == '>=': min_size.extend( represent_property_arguments(row_property)) else: filters.extend(property_to_lambdas(row_property)) else: filters.extend(property_to_lambdas(row_property)) elif other_row.kind == generate_property_table.Kind.UNIVERSAL_QUANTIFIER: if row.var_id in { e for s in other_row.get_dependencies().values() for e in s }: for row_property in other_row.properties.values(): lambdas = property_to_lambdas(row_property) for l in lambdas: if row.var_id in l.free_variables and other_row.var_id in l.free_variables: l.free_variables.remove(other_row.var_id) l.condition = f"all({l.condition} for {other_row.var_id} in {row.var_id})" filters.extend(lambdas) else: elements = _infer_strategy(other_row, table) filters_to_delete = [] for f in elements.filters: if row.var_id in f.free_variables: new_filter = Lambda( condition= f"all({f.condition} for {other_row.var_id} in {row.var_id})", free_variables=[row.var_id]) filters.append(new_filter) filters_to_delete.append(f) elements.filters = [ f for f in elements.filters if f not in filters_to_delete ] else: raise NotImplementedError elif other_row.parent == f'{row.var_id}.keys()': if other_row.kind == generate_property_table.Kind.UNIVERSAL_QUANTIFIER: keys = _infer_strategy(other_row, table) else: raise NotImplementedError elif other_row.parent == f'{row.var_id}.values()': if other_row.kind == generate_property_table.Kind.UNIVERSAL_QUANTIFIER: values = _infer_strategy(other_row, table) else: raise NotImplementedError if not keys: keys_type = typing.get_args(row.type)[0] keys = _strategy_from_type_hint(keys_type) if not values: values_type = typing.get_args(row.type)[1] values = _strategy_from_type_hint(values_type) return SymbolicDictionaryStrategy(var_id=row.var_id, keys=keys, values=values, min_size=min_size, max_size=max_size, filters=filters)
def _infer_list_strategy( row: generate_property_table.Row, table: generate_property_table.Table) -> SymbolicListStrategy: elements: Optional[SymbolicStrategy] = None min_size: List[str] = [] max_size: List[str] = [] unique_by: List[UniqueBy] = [] unique: bool = False filters: List[Lambda] = [] for property_identifier, row_property in row.properties.items(): if property_identifier == 'IS_UNIQUE': unique = True else: raise NotImplementedError( "'is_unique' is currently the only property applying directly to a list." ) for other_row in table.get_rows(): if other_row.parent == row.var_id: if other_row.kind == generate_property_table.Kind.LINK: link_property = other_row.var_id[:-(len(other_row.parent) + 2)] for property_identifier, row_property in other_row.properties.items( ): parents = [] parent = other_row.parent while parent: parents.append(parent) parent = table.get_row_by_var_id(parent).parent if any(parent in row_property.free_variables() for parent in parents): filters.extend(property_to_lambdas(row_property)) elif link_property == 'len': if property_identifier == '<': row_property_decremented = _decrement_property( row_property) max_size.extend( represent_property_arguments( row_property_decremented)) elif property_identifier == '<=': max_size.extend( represent_property_arguments(row_property)) elif property_identifier == '>': row_property_increment = _increment_property( row_property) min_size.extend( represent_property_arguments( row_property_increment)) elif property_identifier == '>=': min_size.extend( represent_property_arguments(row_property)) else: filters.extend(property_to_lambdas(row_property)) else: filters.extend(property_to_lambdas(row_property)) elif other_row.kind == generate_property_table.Kind.UNIVERSAL_QUANTIFIER: if row.var_id in { e for s in other_row.get_dependencies().values() for e in s }: for row_property in other_row.properties.values(): lambdas = property_to_lambdas(row_property) for l in lambdas: if row.var_id in l.free_variables and other_row.var_id in l.free_variables: l.free_variables.remove(other_row.var_id) l.condition = f"all({l.condition} for {other_row.var_id} in {row.var_id})" filters.extend(lambdas) else: elements = _infer_strategy(other_row, table) filters_to_delete = [] for f in elements.filters: if row.var_id in f.free_variables: new_filter = Lambda( condition= f"all({f.condition} for {other_row.var_id} in {row.var_id})", free_variables=[row.var_id]) filters.append(new_filter) filters_to_delete.append(f) elements.filters = [ f for f in elements.filters if f not in filters_to_delete ] else: raise NotImplementedError if not elements: elements_type = typing.get_args(row.type)[0] elements = _strategy_from_type_hint(elements_type) return SymbolicListStrategy(var_id=row.var_id, elements=elements, min_size=min_size, max_size=max_size, unique_by=unique_by, unique=False or unique, filters=filters)
def _infer_from_regex_strategy( row: generate_property_table.Row, table: generate_property_table.Table) -> SymbolicFromRegexStrategy: regexps: List[str] = [] full_match: bool = False filters: List[Lambda] = [] for property_identifier, row_property in row.properties.items(): if property_identifier == 'isalnum': regexps.append(r'^[0-9a-zA-Z]+$') full_match = True elif property_identifier == 'isalpha': regexps.append(r'^[a-zA-Z]+$') full_match = True elif property_identifier == 'isdigit': regexps.append(r'^[0-9]*$') full_match = True elif property_identifier == 'islower': regexps.append(r'^[a-z]$') full_match = True elif property_identifier == 'isnumeric': regexps.append(r'^(-[0-9]*|[0-9]*)$') full_match = True elif property_identifier == 'isspace': regexps.append(r'^\s+$') full_match = True elif property_identifier == 'isupper': regexps.append(r'^[A-Z]+$') full_match = True elif property_identifier == 'isdecimal': regexps.append(r'^\d*\.?\d+$') full_match = True elif property_identifier in ['re.match', 'regex.match']: # re.match(r'..', s), we only want the first argument and we don't want any leading/ending \' regexps.extend([ arg.rsplit(',', 1)[0][1:] for arg in represent_property_arguments(row_property) ]) elif property_identifier in ['re.fullmatch', 'regex.fullmatch']: # re.match(r'..', s), we only want the first argument and we don't want any leading/ending \' regexps.extend([ arg.rsplit(',', 1)[0][1:] for arg in represent_property_arguments(row_property) ]) full_match = True elif property_identifier == 'contains' or property_identifier == 'in': regexps.extend([ arg.strip("\'") for arg in represent_property_arguments(row_property) ]) elif property_identifier == 'startswith': stripped_args = [ arg.strip('\"') for arg in represent_property_arguments(row_property) ] regexps.extend([f'\"^{arg}\"' for arg in stripped_args]) elif property_identifier == 'endswith': stripped_args = [ arg.strip('\"') for arg in represent_property_arguments(row_property) ] regexps.extend([f'\".*{arg}$\"' for arg in stripped_args]) full_match = True elif row.kind == generate_property_table.Kind.LINK: link_property = row.var_id[:-(len(row.parent) + 2)] if link_property == 'len': if property_identifier == '<': filters.extend(property_to_lambdas(row_property)) elif property_identifier == '<=': filters.extend(property_to_lambdas(row_property)) elif property_identifier == '>': filters.extend(property_to_lambdas(row_property)) elif property_identifier == '>=': filters.extend(property_to_lambdas(row_property)) else: raise NotImplementedError else: raise NotImplementedError else: filters.extend(property_to_lambdas(row_property)) for link_row in table.get_rows(): if link_row.parent == row.var_id and link_row.kind == generate_property_table.Kind.LINK: link_strategy = _infer_from_regex_strategy(link_row, table) regexps.extend(link_strategy.regexps) full_match = full_match or link_strategy.full_match filters.extend(link_strategy.filters) return SymbolicFromRegexStrategy(var_id=row.var_id, regexps=regexps, full_match=full_match, filters=filters)
def _infer_text_strategy( row: generate_property_table.Row, table: generate_property_table.Table) -> SymbolicTextStrategy: blacklist_categories: List[Set[str]] = [] whitelist_categories: List[Set[str]] = [] min_size: List[Union[int, str]] = [] max_size: List[Union[int, str]] = [] filters: List[Tuple[str, Set[str]]](row.var_id) = [] for property_identifier, row_property in row.properties.items(): if property_identifier == 'isalnum': whitelist_categories.append({'Ll', 'Lu', 'Nd'}) elif property_identifier == 'isalpha': whitelist_categories.append({'Ll', 'Lu'}) elif property_identifier == 'isdigit': whitelist_categories.append({'Nd'}) elif property_identifier == 'islower': whitelist_categories.append({'Ll'}) elif property_identifier == 'isnumeric': whitelist_categories.append({'Nd', 'Nl', 'No'}) elif property_identifier == 'isspace': whitelist_categories.append({'Zs'}) elif property_identifier == 'isupper': whitelist_categories.append({'Lu'}) elif property_identifier == 'isdecimal': whitelist_categories.append({'Nd'}) elif row.kind == generate_property_table.Kind.LINK: link_property = row.var_id[:-(len(row.parent) + 2)] if link_property == 'len': if property_identifier == '<': row_property_decremented = _decrement_property( row_property) max_size.extend( represent_property_arguments(row_property_decremented)) elif property_identifier == '<=': max_size.extend(represent_property_arguments(row_property)) elif property_identifier == '>': row_property_increment = _increment_property(row_property) min_size.extend( represent_property_arguments(row_property_increment)) elif property_identifier == '>=': min_size.extend(represent_property_arguments(row_property)) elif property_identifier == '==': min_size.extend(represent_property_arguments(row_property)) max_size.extend(represent_property_arguments(row_property)) else: raise NotImplementedError else: raise NotImplementedError else: filters.extend(property_to_lambdas(row_property)) for link_row in table.get_rows(): if link_row.parent == row.var_id and link_row.kind == generate_property_table.Kind.LINK: link_strategy = _infer_text_strategy(link_row, table) blacklist_categories.extend(link_strategy.blacklist_categories) whitelist_categories.extend(link_strategy.whitelist_categories) min_size.extend(link_strategy.min_size) max_size.extend(link_strategy.max_size) filters.extend(link_strategy.filters) return SymbolicTextStrategy(var_id=row.var_id, blacklist_categories=blacklist_categories, whitelist_categories=whitelist_categories, min_size=min_size, max_size=max_size, filters=filters)