## vim: filetype=makopython

    # No matter the Python version, take bytes (forwarded as-is to C) but
    # also accept text (encoded using the system encoding).

    @staticmethod
    def _coerce_bytes(label, value, what='a bytes string', or_none=False):
        if value is None and or_none:
            return None
        elif isinstance(value, _py2to3.bytes_type):
            return value
        elif isinstance(value, _py2to3.text_type):
            return value.encode()
        else:
            raise TypeError('`{}` argument must be {} (got {})'
                            .format(label, what, _type_fullname(type(value))))

    @classmethod
    def for_project(cls, project_file, project=None, scenario_vars=None,
                    target=None, runtime=None):
        ${py_doc('libadalang.create_project_unit_provider', 8)}

        project_file = cls._coerce_bytes('project_file', project_file)
        project_file_text = _py2to3.bytes_to_text(project_file)
        project = cls._coerce_bytes('project', project, or_none=True)
        target = cls._coerce_bytes('target', target, or_none=True)
        runtime = cls._coerce_bytes('runtime', runtime, or_none=True)

        if scenario_vars:
            items = scenario_vars.items()
            scn_vars_array_type = _project_scenario_variable * (len(items) + 1)
            scn_vars_array = scn_vars_array_type()
            for i, (name, value) in enumerate(items):
                what = 'a dict mapping bytes strings to bytes strings'
                name = cls._coerce_bytes('scenario_vars', name, what)
                value = cls._coerce_bytes('scenario_vars', value, what)
                scn_vars_array[i] = _project_scenario_variable(name, value)
            scn_vars_array[-1] = _project_scenario_variable(None, None)
        else:
            scn_vars_array = None

        c_value = _create_project_unit_provider(
            project_file, project, scn_vars_array, target, runtime
        )
        if c_value:
            return cls(c_value)
        else:
            raise InvalidProjectError(
                'Cannot open project {}'.format(project_file_text)
            )

    @classmethod
    def auto(cls, input_files, charset=None):
        ${py_doc('libadalang.create_auto_provider', 8)}

        # Create a NULL-terminated array of strings
        c_strings = [
            ctypes.c_char_p(cls._coerce_bytes('input_files', f,
                                              'a list of bytes strings'))
            for f in input_files
        ]
        c_array_type = ctypes.c_char_p * (len(input_files) + 1)
        c_array = c_array_type()
        for i, c_str in enumerate(c_strings):
            c_array[i] = c_str
        c_array[-1] = None

        c_array_ptr = ctypes.pointer(c_array)
        input_files_arg = ctypes.cast(c_array_ptr,
                                      ctypes.POINTER(ctypes.c_char_p))
        c_value = _create_auto_provider(input_files_arg, charset)
        return cls(c_value)
