"Fossies" - the Fresh Open Source Software Archive

Member "http-prompt-2.1.0/http_prompt/completer.py" (5 Mar 2021, 6379 Bytes) of package /linux/www/http-prompt-2.1.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "completer.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.0.0_vs_2.1.0.

    1 # -*- coding: utf-8 -*-
    2 from __future__ import unicode_literals
    3 
    4 import re
    5 
    6 from collections import OrderedDict
    7 
    8 from itertools import chain
    9 from urllib.parse import urlparse
   10 
   11 from prompt_toolkit.completion import Completer, Completion
   12 
   13 from .completion import (ROOT_COMMANDS, ACTIONS, OPTION_NAMES, HEADER_NAMES,
   14                          HEADER_VALUES)
   15 
   16 
   17 RULES = [
   18     # (regex pattern, a method name in CompletionGenerator)
   19     (r'((?:[^\s\'"\\=:]|(?:\\.))+):((?:[^\s\'"\\]|(?:\\.))*)$',
   20      'header_values'),
   21 
   22     (r'(get|head|post|put|patch|delete|connect)\s+', 'concat_mutations'),
   23     (r'(httpie|curl)\s+', 'preview'),
   24     (r'rm\s+\-b\s+', 'existing_body_params'),
   25     (r'rm\s+\-h\s+', 'existing_header_names'),
   26     (r'rm\s+\-o\s+', 'existing_option_names'),
   27     (r'rm\s+\-q\s+', 'existing_querystring_params'),
   28 
   29     # The last two captures are full URL path and the last part of the URL
   30     # path. For example:
   31     # '/foo/bar' => ('/foo/bar', 'bar')
   32     # '/foo/bar/' => ('/foo/bar/', '')
   33     # 'foo/bar' => ('foo/bar', 'bar')
   34     (r'(ls|cd)\s+(/?(?:[^/]+/)*([^/]*)/?)$', 'urlpaths'),
   35     (r'^\s*[^\s]*$', 'root_commands')
   36 ]
   37 
   38 
   39 def compile_rules(rules):
   40     compiled_rules = []
   41     for pattern, meta_dict in rules:
   42         regex = re.compile(pattern)
   43         compiled_rules.append((regex, meta_dict))
   44     return compiled_rules
   45 
   46 RULES = compile_rules(RULES)
   47 
   48 
   49 def fuzzyfinder(text, collection):
   50     """https://github.com/amjith/fuzzyfinder"""
   51     suggestions = []
   52     if not isinstance(text, str):
   53         text = str(text)
   54     pat = '.*?'.join(map(re.escape, text))
   55     regex = re.compile(pat, flags=re.IGNORECASE)
   56     for item in collection:
   57         r = regex.search(item)
   58         if r:
   59             suggestions.append((len(r.group()), r.start(), item))
   60 
   61     return (z for _, _, z in sorted(suggestions))
   62 
   63 
   64 def match_completions(cur_word, word_dict):
   65     words = word_dict.keys()
   66     suggestions = fuzzyfinder(cur_word, words)
   67     for word in suggestions:
   68         desc = word_dict.get(word, '')
   69         yield Completion(word, -len(cur_word), display_meta=desc)
   70 
   71 
   72 class CompletionGenerator(object):
   73 
   74     def root_commands(self, context, match):
   75         return chain(
   76             self._generic_generate(ROOT_COMMANDS.keys(), {}, ROOT_COMMANDS),
   77             self.actions(context, match),
   78             self.concat_mutations(context, match)
   79         )
   80 
   81     def header_values(self, context, match):
   82         header_name = match.group(1)
   83         header_values = HEADER_VALUES.get(header_name)
   84         if header_values:
   85             for value in header_values:
   86                 yield value, header_name
   87 
   88     def preview(self, context, match):
   89         return chain(
   90             self.actions(context, match),
   91             self.concat_mutations(context, match)
   92         )
   93 
   94     def actions(self, context, match):
   95         return self._generic_generate(ACTIONS.keys(), {}, ACTIONS)
   96 
   97     def concat_mutations(self, context, match):
   98         return chain(
   99             self._generic_generate(context.body_params.keys(),
  100                                    context.body_params, 'Body parameter'),
  101             self._generic_generate(context.querystring_params.keys(),
  102                                    context.querystring_params,
  103                                    'Querystring parameter'),
  104             self._generic_generate(HEADER_NAMES.keys(),
  105                                    context.headers, HEADER_NAMES),
  106             self._generic_generate(OPTION_NAMES.keys(),
  107                                    context.options, OPTION_NAMES)
  108         )
  109 
  110     def existing_body_params(self, context, match):
  111         params = context.body_params.copy()
  112         params.update(context.body_json_params)
  113         return self._generic_generate(params.keys(), params, 'Body parameter')
  114 
  115     def existing_querystring_params(self, context, match):
  116         return self._generic_generate(
  117             context.querystring_params.keys(),
  118             context.querystring_params, 'Querystring parameter')
  119 
  120     def existing_header_names(self, context, match):
  121         return self._generic_generate(context.headers.keys(),
  122                                       context.headers, HEADER_NAMES)
  123 
  124     def existing_option_names(self, context, match):
  125         return self._generic_generate(context.options.keys(),
  126                                       context.options, OPTION_NAMES)
  127 
  128     def urlpaths(self, context, match):
  129         path = urlparse(context.url).path.split('/')
  130         overrided_path = match.group(2)
  131         if overrided_path:
  132             if overrided_path.startswith('/'):
  133                 # Absolute path
  134                 path = []
  135             path += overrided_path.split('/')[:-1]
  136         names = [
  137             node.name for node in context.root.ls(*path)
  138             if node.data.get('type') == 'dir'
  139         ]
  140         return self._generic_generate(names, {}, 'Endpoint')
  141 
  142     def _generic_generate(self, names, values, descs):
  143         for name in sorted(names):
  144             if isinstance(descs, str):
  145                 desc = descs
  146             else:
  147                 desc = descs.get(name, '')
  148             if name in values:
  149                 value = values[name]
  150                 if value is None:
  151                     desc += ' (on)'
  152                 else:
  153                     value = str(value)
  154                     if len(value) > 16:
  155                         value = value[:13] + '...'
  156                     desc += ' (=%s)' % value
  157             yield name, desc
  158 
  159 
  160 class HttpPromptCompleter(Completer):
  161 
  162     def __init__(self, context):
  163         self.context = context
  164         self.comp_gen = CompletionGenerator()
  165 
  166     def get_completions(self, document, complete_event):
  167         cur_text = document.text_before_cursor
  168         cur_word = None
  169         word_dict = None
  170 
  171         for regex, method_name in RULES:
  172             match = regex.search(cur_text)
  173             if match:
  174                 gen_completions = getattr(self.comp_gen, method_name)
  175                 completions = gen_completions(self.context, match)
  176                 word_dict = OrderedDict(completions)
  177 
  178                 groups = match.groups()
  179                 if len(groups) > 1:
  180                     cur_word = groups[-1]
  181                 else:
  182                     cur_word = document.get_word_before_cursor(WORD=True)
  183 
  184                 break
  185 
  186         if word_dict:
  187             for comp in match_completions(cur_word, word_dict):
  188                 yield comp