コード例 #1
0
ファイル: helper.py プロジェクト: yk/jina
def parse_config_source(path: Union[str, TextIO],
                        allow_stream: bool = True,
                        allow_yaml_file: bool = True,
                        allow_builtin_resource: bool = True,
                        allow_raw_yaml_content: bool = True,
                        allow_raw_driver_yaml_content: bool = True,
                        allow_class_type: bool = True,
                        *args,
                        **kwargs) -> Tuple[TextIO, Optional[str]]:
    """ Check if the text or text stream is valid

    :return: a tuple, the first element is the text stream, the second element is the file path associate to it
            if available.
    """
    import io
    from pkg_resources import resource_filename, resource_stream
    if not path:
        raise BadConfigSource
    elif allow_stream and hasattr(path, 'read'):
        # already a readable stream
        return path, None
    elif allow_yaml_file and (path.endswith('.yml') or path.endswith('.yaml')):
        comp_path = _complete_path(path)
        return open(comp_path, encoding='utf8'), comp_path
    elif allow_builtin_resource and path.startswith('_') and os.path.exists(
            resource_filename('jina', '/'.join(
                ('resources', f'executors.{path}.yml')))):
        # NOTE: this returns a binary stream
        comp_path = resource_filename(
            'jina', '/'.join(('resources', f'executors.{path}.yml')))
        return open(comp_path, encoding='utf8'), comp_path
    elif allow_raw_yaml_content and path.startswith('!'):
        # possible YAML content
        path = path.replace('|', '\n    with: ')
        return io.StringIO(path), None
    elif allow_raw_driver_yaml_content and path.startswith('- !'):
        # possible driver YAML content, right now it is only used for debugging
        with open(
                resource_filename(
                    'jina', '/'.join(
                        ('resources',
                         'executors.base.all.yml' if path.startswith('- !!')
                         else 'executors.base.yml')))) as fp:
            _defaults = fp.read()
        path = path.replace('- !!', '- !').replace(
            '|', '\n        with: ')  # for indent, I know, its nasty
        path = _defaults.replace('*', path)
        return io.StringIO(path), None
    elif allow_class_type and path.isidentifier():
        # possible class name
        return io.StringIO(f'!{path}'), None
    else:
        raise BadConfigSource(
            f'{path} can not be resolved, it should be a readable stream,'
            ' or a valid file path, or a supported class name.')
コード例 #2
0
ファイル: __init__.py プロジェクト: jina-ai/jina
    def load_config(
        cls,
        source: Union[str, TextIO, Dict],
        *,
        allow_py_modules: bool = True,
        substitute: bool = True,
        context: Optional[Dict[str, Any]] = None,
        uses_with: Optional[Dict] = None,
        uses_metas: Optional[Dict] = None,
        uses_requests: Optional[Dict] = None,
        extra_search_paths: Optional[List[str]] = None,
        py_modules: Optional[str] = None,
        runtime_args: Optional[Dict[str, Any]] = None,
        **kwargs,
    ) -> 'JAMLCompatible':
        """A high-level interface for loading configuration with features
        of loading extra py_modules, substitute env & context variables. Any class that
        implements :class:`JAMLCompatible` mixin can enjoy this feature, e.g. :class:`BaseFlow`,
        :class:`BaseExecutor`, :class:`BaseDriver` and all their subclasses.

        Support substitutions in YAML:
            - Environment variables: ``${{ ENV.VAR }}`` (recommended), ``$VAR`` (deprecated).
            - Context dict (``context``): ``${{ CONTEXT.VAR }}``(recommended), ``${{ VAR }}``.
            - Internal reference via ``this`` and ``root``: ``${{this.same_level_key}}``, ``${{root.root_level_key}}``

        Substitutions are carried in the order and multiple passes to resolve variables with best effort.

        .. highlight:: yaml
        .. code-block:: yaml

            !BaseEncoder
            metas:
                name: ${{VAR_A}}  # env or context variables
                workspace: my-${{this.name}}  # internal reference

        .. highlight:: python
        .. code-block:: python

            # load Executor from yaml file
            BaseExecutor.load_config('a.yml')

            # load Executor from yaml file and substitute environment variables
            os.environ['VAR_A'] = 'hello-world'
            b = BaseExecutor.load_config('a.yml')
            assert b.name == 'hello-world'

            # load Executor from yaml file and substitute variables from a dict
            b = BaseExecutor.load_config('a.yml', context={'VAR_A': 'hello-world'})
            assert b.name == 'hello-world'

            # disable substitute
            b = BaseExecutor.load_config('a.yml', substitute=False)


        .. # noqa: DAR401
        :param source: the multi-kind source of the configs.
        :param allow_py_modules: allow importing plugins specified by ``py_modules`` in YAML at any levels
        :param substitute: substitute environment, internal reference and context variables.
        :param context: context replacement variables in a dict, the value of the dict is the replacement.
        :param uses_with: dictionary of parameters to overwrite from the default config's with field
        :param uses_metas: dictionary of parameters to overwrite from the default config's metas field
        :param uses_requests: dictionary of parameters to overwrite from the default config's requests field
        :param extra_search_paths: extra paths used when looking for executor yaml files
        :param py_modules: Optional py_module from which the object need to be loaded
        :param runtime_args: Optional dictionary of parameters runtime_args to be directly passed without being parsed into a yaml config
        :param : runtime_args that need to be passed to the yaml

        :param kwargs: kwargs for parse_config_source
        :return: :class:`JAMLCompatible` object
        """
        if runtime_args:
            kwargs['runtimes_args'] = (
                dict()
            )  # when we have runtime args it is needed to have an empty runtime args session in the yam config

        if py_modules:
            kwargs['runtimes_args']['py_modules'] = py_modules

        if isinstance(source, str) and os.path.exists(source):
            extra_search_paths = (extra_search_paths
                                  or []) + [os.path.dirname(source)]

        stream, s_path = parse_config_source(
            source, extra_search_paths=extra_search_paths, **kwargs)
        with stream as fp:
            # first load yml with no tag
            no_tag_yml = JAML.load_no_tags(fp)
            if no_tag_yml:
                no_tag_yml.update(**kwargs)

                # if there is `override_with` u should make sure that `uses_with` does not remain in the yaml
                def _delitem(
                    obj,
                    key,
                ):
                    value = obj.get(key, None)
                    if value:
                        del obj[key]
                        return
                    for k, v in obj.items():
                        if isinstance(v, dict):
                            _delitem(v, key)

                if uses_with is not None:
                    _delitem(no_tag_yml, key='uses_with')
                if uses_metas is not None:
                    _delitem(no_tag_yml, key='uses_metas')
                if uses_requests is not None:
                    _delitem(no_tag_yml, key='uses_requests')
                cls._override_yml_params(no_tag_yml, 'with', uses_with)
                cls._override_yml_params(no_tag_yml, 'metas', uses_metas)
                cls._override_yml_params(no_tag_yml, 'requests', uses_requests)

            else:
                raise BadConfigSource(
                    f'can not construct {cls} from an empty {source}. nothing to read from there'
                )
            if substitute:
                # expand variables
                no_tag_yml = JAML.expand_dict(no_tag_yml, context)

            if allow_py_modules:
                _extra_search_paths = extra_search_paths or []
                load_py_modules(
                    no_tag_yml,
                    extra_search_paths=(_extra_search_paths +
                                        [os.path.dirname(s_path)])
                    if s_path else _extra_search_paths,
                )

            from jina.orchestrate.flow.base import Flow

            if issubclass(cls, Flow):
                no_tag_yml_copy = copy.copy(no_tag_yml)
                # only needed for Flow
                if no_tag_yml_copy.get('with') is None:
                    no_tag_yml_copy['with'] = {}
                no_tag_yml_copy['with']['extra_search_paths'] = (
                    no_tag_yml_copy['with'].get('extra_search_paths')
                    or []) + (extra_search_paths or [])

                if cls.is_valid_jaml(no_tag_yml_copy):
                    no_tag_yml = no_tag_yml_copy

                tag_yml = JAML.unescape(
                    JAML.dump(no_tag_yml),
                    include_unknown_tags=False,
                    jtype_whitelist=('Flow', ),
                )
            else:
                # revert yaml's tag and load again, this time with substitution
                tag_yml = JAML.unescape(JAML.dump(no_tag_yml))
            # load into object, no more substitute
            obj = JAML.load(tag_yml,
                            substitute=False,
                            runtime_args=runtime_args)
            if not isinstance(obj, cls):
                raise BadConfigSource(
                    f'Can not construct {cls} object from {source}. Source might be an invalid configuration.'
                )
            return obj
コード例 #3
0
def parse_config_source(path: Union[str, TextIO, Dict],
                        allow_stream: bool = True,
                        allow_yaml_file: bool = True,
                        allow_builtin_resource: bool = True,
                        allow_raw_yaml_content: bool = True,
                        allow_raw_driver_yaml_content: bool = True,
                        allow_class_type: bool = True,
                        allow_dict: bool = True,
                        allow_json: bool = True,
                        *args,
                        **kwargs) -> Tuple[TextIO, Optional[str]]:
    """Check if the text or text stream is valid.

    # noqa: DAR401
    :param path: the multi-kind source of the configs.
    :param allow_stream: flag
    :param allow_yaml_file: flag
    :param allow_builtin_resource: flag
    :param allow_raw_yaml_content: flag
    :param allow_raw_driver_yaml_content: flag
    :param allow_class_type: flag
    :param allow_dict: flag
    :param allow_json: flag
    :param *args: *args
    :param **kwargs: **kwargs
    :return: a tuple, the first element is the text stream, the second element is the file path associate to it
            if available.
    """
    import io
    from pkg_resources import resource_filename
    if not path:
        raise BadConfigSource
    elif allow_dict and isinstance(path, dict):
        from . import JAML
        tmp = JAML.dump(path)
        return io.StringIO(tmp), None
    elif allow_stream and hasattr(path, 'read'):
        # already a readable stream
        return path, None
    elif allow_yaml_file and is_yaml_filepath(path):
        comp_path = complete_path(path)
        return open(comp_path, encoding='utf8'), comp_path
    elif allow_builtin_resource and path.lstrip(
    ).startswith('_') and os.path.exists(
            resource_filename('jina', '/'.join(
                ('resources', f'executors.{path}.yml')))):
        # NOTE: this returns a binary stream
        comp_path = resource_filename(
            'jina', '/'.join(('resources', f'executors.{path}.yml')))
        return open(comp_path, encoding='utf8'), comp_path
    elif allow_raw_yaml_content and path.lstrip().startswith('!'):
        # possible YAML content
        path = path.replace('|', '\n    with: ')
        return io.StringIO(path), None
    elif allow_raw_driver_yaml_content and path.lstrip().startswith('- !'):
        # possible driver YAML content, right now it is only used for debugging
        with open(
                resource_filename(
                    'jina', '/'.join(('resources', 'executors.base.all.yml'
                                      if path.lstrip().startswith('- !!') else
                                      'executors.base.yml')))) as fp:
            _defaults = fp.read()
        path = path.replace('- !!', '- !').replace(
            '|', '\n        with: ')  # for indent, I know, its nasty
        path = _defaults.replace('*', path)
        return io.StringIO(path), None
    elif allow_class_type and path.isidentifier():
        # possible class name
        return io.StringIO(f'!{path}'), None
    elif allow_json and isinstance(path, str):
        try:
            from . import JAML
            tmp = json.loads(path)
            tmp = JAML.dump(tmp)
            return io.StringIO(tmp), None
        except json.JSONDecodeError:
            raise BadConfigSource(path)
    else:
        raise BadConfigSource(
            f'{path} can not be resolved, it should be a readable stream,'
            ' or a valid file path, or a supported class name.')
コード例 #4
0
def parse_config_source(
    path: Union[str, TextIO, Dict],
    allow_stream: bool = True,
    allow_yaml_file: bool = True,
    allow_raw_yaml_content: bool = True,
    allow_class_type: bool = True,
    allow_dict: bool = True,
    allow_json: bool = True,
    extra_search_paths: Optional[List[str]] = None,
    *args,
    **kwargs,
) -> Tuple[TextIO, Optional[str]]:
    """
    Check if the text or text stream is valid.

    .. # noqa: DAR401
    :param path: the multi-kind source of the configs.
    :param allow_stream: flag
    :param allow_yaml_file: flag
    :param allow_raw_yaml_content: flag
    :param allow_class_type: flag
    :param allow_dict: flag
    :param allow_json: flag
    :param extra_search_paths: extra paths to search for
    :param args: unused
    :param kwargs: unused
    :return: a tuple, the first element is the text stream, the second element is the file path associate to it
            if available.
    """
    import io

    if not path:
        raise BadConfigSource
    elif allow_dict and isinstance(path, dict):
        from jina.jaml import JAML

        tmp = JAML.dump(path)
        return io.StringIO(tmp), None
    elif allow_stream and hasattr(path, 'read'):
        # already a readable stream
        return path, None
    elif allow_yaml_file and is_yaml_filepath(path):
        comp_path = complete_path(path, extra_search_paths)
        return open(comp_path, encoding='utf8'), comp_path
    elif allow_raw_yaml_content and path.lstrip().startswith(('!', 'jtype')):
        # possible YAML content
        path = path.replace('|', '\n    with: ')
        return io.StringIO(path), None
    elif allow_class_type and path.isidentifier():
        # possible class name
        return io.StringIO(f'!{path}'), None
    elif allow_json and isinstance(path, str):
        try:
            from jina.jaml import JAML

            tmp = json.loads(path)
            tmp = JAML.dump(tmp)
            return io.StringIO(tmp), None
        except json.JSONDecodeError:
            raise BadConfigSource(path)
    else:
        raise BadConfigSource(
            f'{path} can not be resolved, it should be a readable stream,'
            ' or a valid file path, or a supported class name.')