Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
requestitems.py231 linesDownload Raw Back to cli
1import os2import functools3from typing import Callable, Dict, IO, List, Optional, Tuple, Union4 5from .argtypes import KeyValueArg6from .constants import (7    SEPARATORS_GROUP_MULTIPART, SEPARATOR_DATA_EMBED_FILE_CONTENTS,8    SEPARATOR_DATA_EMBED_RAW_JSON_FILE, SEPARATOR_GROUP_NESTED_JSON_ITEMS,9    SEPARATOR_DATA_RAW_JSON, SEPARATOR_DATA_STRING, SEPARATOR_FILE_UPLOAD,10    SEPARATOR_FILE_UPLOAD_TYPE, SEPARATOR_HEADER, SEPARATOR_HEADER_EMPTY,11    SEPARATOR_HEADER_EMBED, SEPARATOR_QUERY_PARAM,12    SEPARATOR_QUERY_EMBED_FILE, RequestType13)14from .dicts import (15    BaseMultiDict, MultipartRequestDataDict, RequestDataDict,16    RequestFilesDict, HTTPHeadersDict, RequestJSONDataDict,17    RequestQueryParamsDict,18)19from .exceptions import ParseError20from .nested_json import interpret_nested_json21from ..utils import get_content_type, load_json_preserve_order_and_dupe_keys, split_iterable22 23 24class RequestItems:25 26    def __init__(self, request_type: Optional[RequestType] = None):27        self.headers = HTTPHeadersDict()28        self.request_type = request_type29        self.is_json = request_type is None or request_type is RequestType.JSON30        self.data = RequestJSONDataDict() if self.is_json else RequestDataDict()31        self.files = RequestFilesDict()32        self.params = RequestQueryParamsDict()33        # To preserve the order of fields in file upload multipart requests.34        self.multipart_data = MultipartRequestDataDict()35 36    @classmethod37    def from_args(38        cls,39        request_item_args: List[KeyValueArg],40        request_type: Optional[RequestType] = None,41    ) -> 'RequestItems':42        instance = cls(request_type=request_type)43        rules: Dict[str, Tuple[Callable, dict]] = {44            SEPARATOR_HEADER: (45                process_header_arg,46                instance.headers,47            ),48            SEPARATOR_HEADER_EMPTY: (49                process_empty_header_arg,50                instance.headers,51            ),52            SEPARATOR_HEADER_EMBED: (53                process_embed_header_arg,54                instance.headers,55            ),56            SEPARATOR_QUERY_PARAM: (57                process_query_param_arg,58                instance.params,59            ),60            SEPARATOR_QUERY_EMBED_FILE: (61                process_embed_query_param_arg,62                instance.params,63            ),64            SEPARATOR_FILE_UPLOAD: (65                process_file_upload_arg,66                instance.files,67            ),68            SEPARATOR_DATA_STRING: (69                process_data_item_arg,70                instance.data,71            ),72            SEPARATOR_DATA_EMBED_FILE_CONTENTS: (73                process_data_embed_file_contents_arg,74                instance.data,75            ),76            SEPARATOR_GROUP_NESTED_JSON_ITEMS: (77                process_data_nested_json_embed_args,78                instance.data,79            ),80            SEPARATOR_DATA_RAW_JSON: (81                convert_json_value_to_form_if_needed(82                    in_json_mode=instance.is_json,83                    processor=process_data_raw_json_embed_arg84                ),85                instance.data,86            ),87            SEPARATOR_DATA_EMBED_RAW_JSON_FILE: (88                convert_json_value_to_form_if_needed(89                    in_json_mode=instance.is_json,90                    processor=process_data_embed_raw_json_file_arg,91                ),92                instance.data,93            ),94        }95 96        if instance.is_json:97            json_item_args, request_item_args = split_iterable(98                iterable=request_item_args,99                key=lambda arg: arg.sep in SEPARATOR_GROUP_NESTED_JSON_ITEMS100            )101            if json_item_args:102                pairs = [(arg.key, rules[arg.sep][0](arg)) for arg in json_item_args]103                processor_func, target_dict = rules[SEPARATOR_GROUP_NESTED_JSON_ITEMS]104                value = processor_func(pairs)105                target_dict.update(value)106 107        # Then handle all other items.108        for arg in request_item_args:109            processor_func, target_dict = rules[arg.sep]110            value = processor_func(arg)111 112            if arg.sep in SEPARATORS_GROUP_MULTIPART:113                instance.multipart_data[arg.key] = value114 115            if isinstance(target_dict, BaseMultiDict):116                target_dict.add(arg.key, value)117            else:118                target_dict[arg.key] = value119 120        return instance121 122 123JSONType = Union[str, bool, int, list, dict]124 125 126def process_header_arg(arg: KeyValueArg) -> Optional[str]:127    return arg.value or None128 129 130def process_embed_header_arg(arg: KeyValueArg) -> str:131    return load_text_file(arg).rstrip('\n')132 133 134def process_empty_header_arg(arg: KeyValueArg) -> str:135    if not arg.value:136        return arg.value137    raise ParseError(138        f'Invalid item {arg.orig!r} (to specify an empty header use `Header;`)'139    )140 141 142def process_query_param_arg(arg: KeyValueArg) -> str:143    return arg.value144 145 146def process_embed_query_param_arg(arg: KeyValueArg) -> str:147    return load_text_file(arg).rstrip('\n')148 149 150def process_file_upload_arg(arg: KeyValueArg) -> Tuple[str, IO, str]:151    parts = arg.value.split(SEPARATOR_FILE_UPLOAD_TYPE)152    filename = parts[0]153    mime_type = parts[1] if len(parts) > 1 else None154    try:155        f = open(os.path.expanduser(filename), 'rb')156    except OSError as e:157        raise ParseError(f'{arg.orig!r}: {e}')158    return (159        os.path.basename(filename),160        f,161        mime_type or get_content_type(filename),162    )163 164 165def convert_json_value_to_form_if_needed(in_json_mode: bool, processor: Callable[[KeyValueArg], JSONType]) -> Callable[[], str]:166    """167    We allow primitive values to be passed to forms via JSON key/value syntax.168 169    But complex values lead to an error because there’s no clear way to serialize them.170 171    """172    if in_json_mode:173        return processor174 175    @functools.wraps(processor)176    def wrapper(*args, **kwargs) -> str:177        try:178            output = processor(*args, **kwargs)179        except ParseError:180            output = None181        if isinstance(output, (str, int, float)):182            return str(output)183        else:184            raise ParseError('Cannot use complex JSON value types with --form/--multipart.')185 186    return wrapper187 188 189def process_data_item_arg(arg: KeyValueArg) -> str:190    return arg.value191 192 193def process_data_embed_file_contents_arg(arg: KeyValueArg) -> str:194    return load_text_file(arg)195 196 197def process_data_embed_raw_json_file_arg(arg: KeyValueArg) -> JSONType:198    contents = load_text_file(arg)199    value = load_json(arg, contents)200    return value201 202 203def process_data_raw_json_embed_arg(arg: KeyValueArg) -> JSONType:204    value = load_json(arg, arg.value)205    return value206 207 208def process_data_nested_json_embed_args(pairs) -> Dict[str, JSONType]:209    return interpret_nested_json(pairs)210 211 212def load_text_file(item: KeyValueArg) -> str:213    path = item.value214    try:215        with open(os.path.expanduser(path), 'rb') as f:216            return f.read().decode()217    except OSError as e:218        raise ParseError(f'{item.orig!r}: {e}')219    except UnicodeDecodeError:220        raise ParseError(221            f'{item.orig!r}: cannot embed the content of {item.value!r},'222            ' not a UTF-8 or ASCII-encoded text file'223        )224 225 226def load_json(arg: KeyValueArg, contents: str) -> JSONType:227    try:228        return load_json_preserve_order_and_dupe_keys(contents)229    except ValueError as e:230        raise ParseError(f'{arg.orig!r}: {e}')231 
codekingpro/portable-devtools · Team Ai