def filter_types(cls, values_type) -> Set[IType]:
        if values_type is None:
            values_type = set()
        elif not isinstance(values_type, set):
            if isinstance(values_type, Iterable):
                values_type = set(values_type)
            else:
                values_type = {values_type}

        if len(values_type) > 1:
            from boa3.model.type.type import Type
            if any(t is Type.any or t is Type.none for t in values_type):
                return {Type.any}

            actual_types = list(values_type)[:1]
            for value in list(values_type)[1:]:
                other = next((x for x in actual_types
                              if x.is_type_of(value) or value.is_type_of(x)),
                             None)

                if other is not None and value.is_type_of(other):
                    actual_types.remove(other)
                    other = None

                if other is None:
                    actual_types.append(value)
            values_type = set(actual_types)

            if any(not isinstance(x, ICollectionType) for x in values_type):
                return values_type
            if all(isinstance(x, PrimitiveType) for x in values_type):
                return values_type
            # verifies if all the types are the same collection with different arguments
            if not all(isinstance(x, ICollectionType) for x in values_type):
                return {Type.get_generic_type(*values_type)}
            else:
                first_item: ICollectionType = list(values_type)[0]
                collection_type = type(first_item)  # first sequence type
                value_type = type(
                    first_item.item_type)  # first sequence values type

            if all(isinstance(x, collection_type) for x in values_type):
                # if all the types are the same sequence type, build this sequence with any as parameters
                types = set(value.item_type for value in values_type)
                values_type = {collection_type.build(types)}
            else:
                generic_type: IType = Type.get_generic_type(*values_type)
                if (isinstance(generic_type, ICollectionType) and all(
                        isinstance(x.item_type, value_type)
                        for x in values_type)):
                    # the collections doesn't have the same type but the value type is the same
                    # for example: Tuple[int] and List[int]
                    values_type = {
                        generic_type.build_collection(first_item.item_type)
                    }
                else:
                    # otherwise, built a generic sequence with any as parameters
                    values_type = {generic_type}

        return values_type
Exemple #2
0
    def __init__(self, import_target: str):
        self.can_be_imported: bool = False
        self._import_identifier: str = import_target

        try:
            module_origin: str = importlib.util.find_spec(import_target).origin
        except BaseException:
            return

        path: List[str] = module_origin.split(os.sep)
        self.path: str = module_origin.replace(os.sep, '/')

        super().__init__(ast.Module(body=[]), path[-1])
        if import_target == 'typing':
            self.symbols.update(
                {symbol_id: symbol for symbol_id, symbol in self._get_types_from_typing_lib().items()
                 if symbol_id not in Type.builtin_types()
                 })
            self.can_be_imported = True

        else:
            import re

            inside_python_folder = any(re.search(r'python(\d\.?)*', folder.lower()) for folder in path)
            updated_tree = None

            if 'boa3' in path and '/'.join(path[path.index('boa3'):]).startswith('boa3/builtin'):
                pkg_start_index = path.index('builtin') + 1
                if path[pkg_start_index] == path[-1]:
                    self.symbols = self._get_boa3_builtin_symbols()
                else:
                    pkg = import_target.split('.')
                    pkg = pkg[pkg.index('builtin') + 1:]
                    self.symbols = self._get_boa3_builtin_package(pkg)
                self.can_be_imported = True

            elif not (inside_python_folder and 'lib' in path):
                # TODO: only user modules and typing lib imports are implemented
                try:
                    from boa3.analyser.analyser import Analyser
                    analyser = Analyser.analyse(module_origin)

                    # include only imported symbols
                    if analyser.is_analysed:
                        self.symbols.update(
                            {symbol_id: symbol for symbol_id, symbol in analyser.symbol_table.items()
                             if symbol_id not in Type.all_types()
                             })
                    updated_tree = analyser.ast_tree
                    self.can_be_imported = analyser.is_analysed
                except FileNotFoundError:
                    self.can_be_imported = False

                if updated_tree is not None:
                    self._tree = updated_tree
Exemple #3
0
    def _get_types_from_typing_lib(self) -> Dict[str, ISymbol]:
        import typing
        from types import FunctionType

        type_symbols: Dict[str, ISymbol] = {}
        all_types: List[str] = typing.__all__

        for t_id in all_types:
            attr = getattr(typing, t_id)
            if not isinstance(attr, FunctionType):
                type_id: str = t_id if t_id in Type.all_types() else t_id.lower()
                if type_id in Type.all_types():
                    type_symbols[t_id] = Type.all_types()[type_id]

        return type_symbols
    def get_type(self, value: Any) -> IType:
        """
        Returns the type of the given value.

        :param value: value to get the type
        :return: Returns the :class:`IType` of the the type of the value. `Type.none` by default.
        """
        # visits if it is a node
        if isinstance(value, ast.AST):
            fun_rtype_id: Any = ast.NodeVisitor.visit(self, value)
            if isinstance(fun_rtype_id, ast.Name):
                fun_rtype_id = fun_rtype_id.id

            if isinstance(fun_rtype_id,
                          str) and not isinstance(value, ast.Str):
                value = self.get_symbol(fun_rtype_id)
            else:
                value = fun_rtype_id

        if (isinstance(value, Attribute)
                and isinstance(value.attr_symbol, IExpression)
                and isinstance(value.attr_symbol.type, ClassType)):
            value = value.attr_symbol

        if isinstance(value, IType):
            return value
        elif isinstance(value, IExpression):
            return value.type
        elif isinstance(value, IOperation):
            return value.result
        else:
            return Type.get_type(value)
Exemple #5
0
    def filter_types(cls, values_type) -> Set[IType]:
        if values_type is None:
            values_type = set()
        elif not isinstance(values_type, set):
            if isinstance(values_type, Iterable):
                values_type = set(values_type)
            else:
                values_type = {values_type}

        if len(values_type) > 1 and all(isinstance(x, MappingType) for x in values_type):
            first_item: MappingType = list(values_type)[0]
            mapping_type = type(first_item)          # first mapping type

            k_types = set(value.key_type for value in values_type)
            v_types = set(value.value_type for value in values_type)

            if all(isinstance(x, mapping_type) for x in values_type):
                values_type = {mapping_type(keys_type=k_types, values_type=v_types)}
            else:
                from boa3.model.type.type import Type
                generic_type: IType = Type.get_generic_type(*values_type)
                if isinstance(generic_type, MappingType):
                    values_type = {generic_type.build_collection(k_types, v_types)}

            return values_type

        # if any value is not a map, call the collection filter
        return super().filter_types(values_type)
    def get_types(cls, value: Any) -> Set[IType]:
        from boa3.model.type.type import Type
        if isinstance(value, IType):
            return {value}

        if not isinstance(value, Iterable):
            value = {value}

        types: Set[IType] = {
            val if isinstance(val, IType) else Type.get_type(val)
            for val in value
        }
        return cls.filter_types(types)
Exemple #7
0
    def visit_IfExp(self, if_node: ast.IfExp):
        """
        Verifies if the type of if test is valid

        :param if_node: the python ast if expression node
        """
        self.validate_if(if_node)
        body = if_node.body
        orelse = if_node.orelse
        if_value = body[-1] if isinstance(body, list) and len(body) > 0 else body
        else_value = orelse[-1] if isinstance(orelse, list) and len(orelse) > 0 else orelse

        if_type: IType = self.get_type(if_value)
        else_type: IType = self.get_type(else_value)
        return Type.get_generic_type(if_type, else_type)
Exemple #8
0
    def visit_Dict(self, dict_node: ast.Dict) -> Dict[Any, Any]:
        """
        Visitor of literal dict node

        :param dict_node: the python ast dict node
        :return: a list with each key and value type
        """
        dictionary = {}
        size = min(len(dict_node.keys), len(dict_node.values))
        for index in range(size):
            key = self.get_type(dict_node.keys[index])
            value = self.get_type(dict_node.values[index])
            if key in dictionary and dictionary[key] != value:
                dictionary[key] = Type.get_generic_type(dictionary[key], value)
            else:
                dictionary[key] = value
        return dictionary
Exemple #9
0
    def get_type(self, value: Any, use_metatype: bool = False) -> IType:
        """
        Returns the type of the given value.

        :param value: value to get the type
        :param use_metatype: whether it should return `Type.type` if the value is an IType implementation.
        :return: Returns the :class:`IType` of the the type of the value. `Type.none` by default.
        """
        # visits if it is a node
        if isinstance(value, ast.AST):
            fun_rtype_id: Any = ast.NodeVisitor.visit(self, value)
            if isinstance(fun_rtype_id, ast.Name):
                fun_rtype_id = fun_rtype_id.id

            if isinstance(fun_rtype_id,
                          str) and not isinstance(value, ast.Str):
                value = self.get_symbol(fun_rtype_id, origin_node=value)
                if isinstance(value,
                              IType) and not isinstance(value, MetaType):
                    value = TypeUtils.type.build(
                        value) if use_metatype else value
            else:
                value = fun_rtype_id

        if isinstance(value, Attribute):
            if ((isinstance(value.attr_symbol, IExpression)
                 and isinstance(value.attr_symbol.type, ClassType))
                    or (isinstance(value.attr_symbol, IType))):
                value = value.attr_symbol
            elif isinstance(value.type, IType):
                value = value.type

        if isinstance(value, IType):
            final_type = value
        elif isinstance(value, IExpression):
            final_type = value.type
        elif isinstance(value, IOperation):
            final_type = value.result
        else:
            final_type = Type.get_type(value)

        if isinstance(final_type, MetaType) and not use_metatype:
            return final_type.meta_type
        else:
            return final_type
Exemple #10
0
    def visit_Dict(self, dict_node: ast.Dict) -> Optional[Dict[Any, Any]]:
        """
        Visitor of literal dict node

        :param dict_node: the python ast dict node
        :return: the value of the dict
        """
        dictionary = {}
        size = min(len(dict_node.keys), len(dict_node.values))
        for index in range(size):
            key = self.get_type(dict_node.keys[index])
            value = self.get_type(dict_node.values[index])
            if key in dictionary and dictionary[key] != value:
                dictionary[key] = Type.get_generic_type(dictionary[key], value)
            else:
                dictionary[key] = value

        keys = set(dictionary.keys())
        values = set(dictionary.values())

        if Type.none in keys or Type.none in values:
            # if can't define the type of any key or value, let it to be defined in the type checking
            return None
        return dictionary
Exemple #11
0
 def __include_builtins_symbols(self):
     """
     Include the Python builtins in the global symbol table
     """
     self.symbol_table.update(Type.builtin_types())
Exemple #12
0
    def _find_package(self,
                      module_origin: str,
                      origin_file: Optional[str] = None):
        path: List[str] = module_origin.split(os.sep)

        package = imports.builtin.get_package(self._import_identifier)
        if hasattr(package, 'symbols'):
            if hasattr(package, 'inner_packages'):
                # when have symbol and packages with the same id, prioritize symbol
                self.symbols: Dict[str, ISymbol] = package.inner_packages
                self.symbols.update(package.symbols)
            else:
                self.symbols = package.symbols

            self.can_be_imported = True
            self.is_builtin_import = True
            return

        if (os.path.commonpath([self.path, constants.BOA_PACKAGE_PATH
                                ]) != constants.BOA_PACKAGE_PATH
                or ('boa3' in path and constants.PATH_SEPARATOR.join(
                    path[path.index('boa3'):]).startswith('boa3/builtin'))):
            # doesn't analyse boa3.builtin packages that aren't included in the imports.builtin as an user module
            # TODO: refactor when importing from user modules is accepted
            import re

            inside_python_folder = any(
                re.search(r'python(\d\.?)*', folder.lower())
                for folder in path)
            updated_tree = None

            if not (inside_python_folder and 'lib' in path):
                # check circular imports to avoid recursions inside the compiler
                if self.path in self._import_stack:
                    self.recursive_import = True
                    return

                # TODO: only user modules and typing lib imports are implemented
                try:
                    if self.path in self._imported_files:
                        analyser = self._imported_files[self.path]
                    else:
                        from boa3.analyser.analyser import Analyser
                        origin = origin_file.replace(os.sep,
                                                     constants.PATH_SEPARATOR)
                        files = self._import_stack
                        files.append(origin)
                        if self.is_namespace_package:
                            analyser = Analyser(self.tree, module_origin,
                                                self.root_folder, self._log)
                            if self._include_inner_packages(analyser):
                                analyser.is_analysed = True
                                self._imported_files[self.path] = analyser
                        else:
                            analyser = Analyser.analyse(
                                module_origin,
                                root=self.root_folder,
                                imported_files=self._imported_files,
                                import_stack=files,
                                log=self._log)
                            self._include_inner_packages(analyser)

                        if analyser.is_analysed:
                            self._imported_files[self.path] = analyser

                    # include only imported symbols
                    if analyser.is_analysed:
                        for symbol_id, symbol in analyser.symbol_table.items():
                            if symbol_id not in Type.all_types():
                                if not self._get_from_entry:
                                    symbol.defined_by_entry = False
                                self.symbols[symbol_id] = symbol

                    self.errors.extend(analyser.errors)
                    self.warnings.extend(analyser.warnings)

                    updated_tree = analyser.ast_tree
                    self.analyser = analyser
                    self.can_be_imported = analyser.is_analysed
                except FileNotFoundError:
                    self.can_be_imported = False

                if updated_tree is not None:
                    self._tree = updated_tree