Coder Social home page Coder Social logo

jmespath.terminal's Introduction

JMESPath

JMESPath (pronounced "james path") allows you to declaratively specify how to extract elements from a JSON document.

For example, given this document:

{"foo": {"bar": "baz"}}

The jmespath expression foo.bar will return "baz".

JMESPath also supports:

Referencing elements in a list. Given the data:

{"foo": {"bar": ["one", "two"]}}

The expression: foo.bar[0] will return "one". You can also reference all the items in a list using the * syntax:

{"foo": {"bar": [{"name": "one"}, {"name": "two"}]}}

The expression: foo.bar[*].name will return ["one", "two"]. Negative indexing is also supported (-1 refers to the last element in the list). Given the data above, the expression foo.bar[-1].name will return "two".

The * can also be used for hash types:

{"foo": {"bar": {"name": "one"}, "baz": {"name": "two"}}}

The expression: foo.*.name will return ["one", "two"].

Installation

You can install JMESPath from pypi with:

pip install jmespath

API

The jmespath.py library has two functions that operate on python data structures. You can use search and give it the jmespath expression and the data:

>>> import jmespath
>>> path = jmespath.search('foo.bar', {'foo': {'bar': 'baz'}})
'baz'

Similar to the re module, you can use the compile function to compile the JMESPath expression and use this parsed expression to perform repeated searches:

>>> import jmespath
>>> expression = jmespath.compile('foo.bar')
>>> expression.search({'foo': {'bar': 'baz'}})
'baz'
>>> expression.search({'foo': {'bar': 'other'}})
'other'

This is useful if you're going to use the same jmespath expression to search multiple documents. This avoids having to reparse the JMESPath expression each time you search a new document.

Options

You can provide an instance of jmespath.Options to control how a JMESPath expression is evaluated. The most common scenario for using an Options instance is if you want to have ordered output of your dict keys. To do this you can use either of these options:

>>> import jmespath
>>> jmespath.search('{a: a, b: b}',
...                 mydata,
...                 jmespath.Options(dict_cls=collections.OrderedDict))


>>> import jmespath
>>> parsed = jmespath.compile('{a: a, b: b}')
>>> parsed.search(mydata,
...               jmespath.Options(dict_cls=collections.OrderedDict))

Custom Functions

The JMESPath language has numerous built-in functions, but it is also possible to add your own custom functions. Keep in mind that custom function support in jmespath.py is experimental and the API may change based on feedback.

If you have a custom function that you've found useful, consider submitting it to jmespath.site and propose that it be added to the JMESPath language. You can submit proposals here.

To create custom functions:

  • Create a subclass of jmespath.functions.Functions.
  • Create a method with the name _func_<your function name>.
  • Apply the jmespath.functions.signature decorator that indicates the expected types of the function arguments.
  • Provide an instance of your subclass in a jmespath.Options object.

Below are a few examples:

import jmespath
from jmespath import functions

# 1. Create a subclass of functions.Functions.
#    The function.Functions base class has logic
#    that introspects all of its methods and automatically
#    registers your custom functions in its function table.
class CustomFunctions(functions.Functions):

    # 2 and 3.  Create a function that starts with _func_
    # and decorate it with @signature which indicates its
    # expected types.
    # In this example, we're creating a jmespath function
    # called "unique_letters" that accepts a single argument
    # with an expected type "string".
    @functions.signature({'types': ['string']})
    def _func_unique_letters(self, s):
        # Given a string s, return a sorted
        # string of unique letters: 'ccbbadd' ->  'abcd'
        return ''.join(sorted(set(s)))

    # Here's another example.  This is creating
    # a jmespath function called "my_add" that expects
    # two arguments, both of which should be of type number.
    @functions.signature({'types': ['number']}, {'types': ['number']})
    def _func_my_add(self, x, y):
        return x + y

# 4. Provide an instance of your subclass in a Options object.
options = jmespath.Options(custom_functions=CustomFunctions())

# Provide this value to jmespath.search:
# This will print 3
print(
    jmespath.search(
        'my_add(`1`, `2`)', {}, options=options)
)

# This will print "abcd"
print(
    jmespath.search(
        'foo.bar | unique_letters(@)',
        {'foo': {'bar': 'ccbbadd'}},
        options=options)
)

Again, if you come up with useful functions that you think make sense in the JMESPath language (and make sense to implement in all JMESPath libraries, not just python), please let us know at jmespath.site.

Specification

If you'd like to learn more about the JMESPath language, you can check out the JMESPath tutorial. Also check out the JMESPath examples page for examples of more complex jmespath queries.

The grammar is specified using ABNF, as described in RFC4234. You can find the most up to date grammar for JMESPath here.

You can read the full JMESPath specification here.

Testing

In addition to the unit tests for the jmespath modules, there is a tests/compliance directory that contains .json files with test cases. This allows other implementations to verify they are producing the correct output. Each json file is grouped by feature.

Discuss

Join us on our Gitter channel if you want to chat or if you have any questions.

jmespath.terminal's People

Contributors

brandond avatar jamesls avatar richardbronosky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jmespath.terminal's Issues

explicitly set bg color for every panel

There are some themes where the current font colors end up being the same as the default background color. One example is the Solarized "Light" theme for iTerm2 (based on an input of { "foo": "bar" }:

screen shot 2015-07-24 at 20 10 17

Note how the "foo" key disappears since it's the same color as the background. Change the theme (in this case to Solarized "Dark"), and it shows up:

screen shot 2015-07-24 at 20 11 41

I don't know that much about xterm color sequences (or ncurses, etc.), but one thought is that if there is any explicitly colored text in a panel, consider also coloring the background of that panel using an explicit color to avoid the "invisible text" effect like the first screenshot above.

Exception on invalid input

To reproduce:

  • start jpterm
  • type &f
  • watch the program abort
Traceback (most recent call last):
  File "/usr/bin/jpterm", line 6, in <module>
    jpterm.main()
  File "/usr/lib/python3.8/site-packages/jpterm.py", line 229, in main
    display.main(screen=screen)
  File "/usr/lib/python3.8/site-packages/jpterm.py", line 146, in main
    self.loop.run()
  File "/usr/lib/python3.8/site-packages/urwid/main_loop.py", line 287, in run
    self._run()
  File "/usr/lib/python3.8/site-packages/urwid/main_loop.py", line 385, in _run
    self.event_loop.run()
  File "/usr/lib/python3.8/site-packages/urwid/main_loop.py", line 790, in run
    self._loop()
  File "/usr/lib/python3.8/site-packages/urwid/main_loop.py", line 827, in _loop
    self._watch_files[fd]()
  File "/usr/lib/python3.8/site-packages/urwid/raw_display.py", line 416, in <lambda>
    wrapper = lambda: self.parse_input(
  File "/usr/lib/python3.8/site-packages/urwid/raw_display.py", line 515, in parse_input
    callback(processed, processed_codes)
  File "/usr/lib/python3.8/site-packages/urwid/main_loop.py", line 412, in _update
    self.process_input(keys)
  File "/usr/lib/python3.8/site-packages/urwid/main_loop.py", line 513, in process_input
    k = self._topmost_widget.keypress(self.screen_size, k)
  File "/usr/lib/python3.8/site-packages/urwid/container.py", line 1115, in keypress
    return self.header.keypress((maxcol,),key)
  File "/usr/lib/python3.8/site-packages/urwid/container.py", line 1621, in keypress
    key = self.focus.keypress(tsize, key)
  File "/usr/lib/python3.8/site-packages/urwid/widget.py", line 1484, in keypress
    self.insert_text(key)
  File "/usr/lib/python3.8/site-packages/urwid/widget.py", line 1410, in insert_text
    self.set_edit_text(result_text)
  File "/usr/lib/python3.8/site-packages/urwid/widget.py", line 1365, in set_edit_text
    self._emit("change", text)
  File "/usr/lib/python3.8/site-packages/urwid/widget.py", line 461, in _emit
    signals.emit_signal(self, name, self, *args)
  File "/usr/lib/python3.8/site-packages/urwid/signals.py", line 265, in emit
    result |= self._call_callback(callback, user_arg, user_args, args)
  File "/usr/lib/python3.8/site-packages/urwid/signals.py", line 295, in _call_callback
    return bool(callback(*args_to_pass))
  File "/usr/lib/python3.8/site-packages/jpterm.py", line 137, in _on_edit
    json.dumps(result, indent=2))
  File "/usr/lib/python3.8/json/__init__.py", line 234, in dumps
    return cls(
  File "/usr/lib/python3.8/json/encoder.py", line 201, in encode
    chunks = list(chunks)
  File "/usr/lib/python3.8/json/encoder.py", line 438, in _iterencode
    o = _default(o)
  File "/usr/lib/python3.8/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type _Expression is not JSON serializable

Urwid version

Is it possible this can be bumped to use the latest urwid? 2.0.1 ?

TIA!

Using stdout and stderr for simplified output of expressions and transformed JSON

jpterm can serve two purposes

  • shape proper JMESPath expression
  • generate output based on given expression

Currently, the program allows (only optionally after using Ctrl-P) to output the expression and does not provide an option to output transfrormed JSON document.

Proposal

  • always output created expression into stderr.
  • always output transformed JSON (as shown in right pane at the exit) document to stdout
  • If Ctrl-P was not used during the session, output the current expression to stderr.
  • If Ctrl-P was used, output only those expressions, which were current at the Ctrl-P moment.
  • When user presses Enter, quit the program (so no need for F5)

This could be further enhanced by means of command line switches:

Options:
--no-json-out                               Does not output tranformed JSON
--no-expression-out                    Does not output created expressions
--json-out-file <json-file>             File, to which shall be json output saved
--expression-out-file <expr-file>  File to output expressions to. Use - for stdout.

scrolling large json files

Hi,
I am wondering if it's possible to scroll the "Input JSON" and/or the "JMESPath Result" panes. I didn't see anything like it in the help. Thank you.

Fix requirement Pygments

Now requirement is Pygments<=2.0,>=1.6

This is probably a bug, because current last version Pygments 2.0.2 doesn't match it.

If it is, you must exclude from top border minor version so it will became: Pygments<=2,>=1.6

Use as command line tool

Can I use this without any user interaction?

I'd like to be able to do something like

jpterm my.json -e '{ foo: a, bar: c.d[:2] }'

Make edit window larger / scrollable.

Hello. Thanks for this technology. It has really helped me. So keep up the good work.

I would like to make a feature request to improve jpterm.

I work with jmespath expressions that span over 100 lines. I'll be writing new ones that will exceed this. To make editing / exploring new expressions easier, I would like the editor window to be larger and scrollable both vertical and horizontal. Certain keystrokes would be nice like beginning of line, end of line, beginning of expression, end of expression, page up/down, goto line in expression, beginning of expression in current line (skip leading whitespaces) And if you can support emacs-like keystrokes for navigation that would be sweet.

But for now, just up/down and right/left arrow keys would be nice.

Thanks and keep up the good work!

unable to highlight for copy paste.

This could be solve by having a hotkey that toggles mouse event listening.

Extra 1:
In this mode you might make ctrl-w+direction navigate "window focus" like in vi.

Extra 2:
It would be nice to have ways to get the jpterm to output either the Expression of the Result on exit. Maybe after receiving F5 you could prompt...

Exiting. Return value: [Nothing/Expression/Result/Cancel-Exit]? 

Investigate why jpterm hangs when used in command substitution

jpterm does not work and will just hang when used with $(...). For example, this works:

echo '"foo"' | jpterm

But this will not:

echo $(echo '"foo"' | jpterm)

I'll need to investigate if this is just a limitation in urwid, or if there's something in jpterm.py I can do to enable this scenario. This would enabled things like:

aws s3api delete-objects --bucket foo --delete "$(aws s3api list-object-versions --bucket foo | jpterm)"

So you could get the JSON structure right by messing around in jpterm, and then that result would then be used as the JSON input structure for the --delete arg.

Traceback (which occurs when I try to Ctrl-C out):

$ echo $(echo '"foo"' | jpterm)
Traceback (most recent call last):
  File "/usr/local/bin/jpterm", line 6, in <module>
    jpterm.main()
  File "/usr/local/lib/python2.7/site-packages/jpterm.py", line 194, in main
    display.main(screen=screen)
  File "/usr/local/lib/python2.7/site-packages/jpterm.py", line 137, in main
    self.loop.run()
  File "/usr/local/lib/python2.7/site-packages/urwid/main_loop.py", line 278, in run
    self.screen.run_wrapper(self._run)
  File "/usr/local/lib/python2.7/site-packages/urwid/raw_display.py", line 272, in run_wrapper
    return fn()
  File "/usr/local/lib/python2.7/site-packages/urwid/main_loop.py", line 343, in _run
    self.event_loop.run()
  File "/usr/local/lib/python2.7/site-packages/urwid/main_loop.py", line 673, in run
    self._loop()
  File "/usr/local/lib/python2.7/site-packages/urwid/main_loop.py", line 697, in _loop
    ready, w, err = select.select(fds, [], fds)
KeyboardInterrupt

f5 not quiting in Byobu

Debian 8.0 with Gnome, using Byobu.

Byobu has F5 already assigned and I am unable to use it for jpterm.

On gnome-terminal it works well.

One could try to make F5 recognized on input, but it would be nice, if there are other, more intuitive methods to exit the program, e.g. Ctrl-C, which is currently not handled and prints stacktrace.

I would propose any of:

  • Ctrl-C
  • having a [Quit] button to which I could Tab and Enter to use it.

Install error

Install command

pip3 install jmespath-terminal

output

Collecting jmespath-terminal
  Using cached jmespath-terminal-0.2.1.tar.gz (6.3 kB)
  Preparing metadata (setup.py) ... done
Collecting jmespath<=1.0.0,>=0.4.1
  Using cached jmespath-1.0.0-py3-none-any.whl (23 kB)
Requirement already satisfied: Pygments<3.0,>=2.0 in /opt/homebrew/lib/python3.10/site-packages (from jmespath-terminal) (2.14.0)
Collecting urwid==1.2.2
  Using cached urwid-1.2.2.tar.gz (585 kB)
  Preparing metadata (setup.py) ... error
  error: subprocess-exited-with-error

  × python setup.py egg_info did not run successfully.
  │ exit code: 1
  ╰─> [27 lines of output]
      Traceback (most recent call last):
        File "/opt/homebrew/lib/python3.10/site-packages/setuptools/_distutils/core.py", line 147, in setup
          _setup_distribution = dist = klass(attrs)
        File "/opt/homebrew/lib/python3.10/site-packages/setuptools/dist.py", line 488, in __init__
          _Distribution.__init__(
        File "/opt/homebrew/lib/python3.10/site-packages/setuptools/_distutils/dist.py", line 283, in __init__
          self.finalize_options()
        File "/opt/homebrew/lib/python3.10/site-packages/setuptools/dist.py", line 912, in finalize_options
          ep(self)
        File "/opt/homebrew/lib/python3.10/site-packages/setuptools/dist.py", line 932, in _finalize_setup_keywords
          ep.load()(self, ep.name, value)
        File "/opt/homebrew/lib/python3.10/site-packages/setuptools/dist.py", line 330, in invalid_unless_false
          raise DistutilsSetupError(f"{attr} is invalid.")
      distutils.errors.DistutilsSetupError: use_2to3 is invalid.

      During handling of the above exception, another exception occurred:

      Traceback (most recent call last):
        File "/private/var/folders/j7/m1yn6z2547x5_n_bj4n1s35h0000gn/T/pip-install-6yxv1mso/urwid_7e9d523fb2114174ae37a0fdcdb61f98/setup.py", line 85, in <module>
          setup(**setup_d)
        File "/opt/homebrew/lib/python3.10/site-packages/setuptools/__init__.py", line 108, in setup
          return distutils.core.setup(**attrs)
        File "/opt/homebrew/lib/python3.10/site-packages/setuptools/_distutils/core.py", line 152, in setup
          raise SystemExit("error in {} setup command: {}".format(attrs['name'], msg))
      SystemExit: error in urwid setup command: use_2to3 is invalid.
      error in urwid setup command: use_2to3 is invalid.
      Couldn't build the extension module, trying without it...
      [end of output]

  note: This error originates from a subprocess, and is likely not a problem with pip.
error: metadata-generation-failed

× Encountered error while generating package metadata.
╰─> See above for output.

note: This is an issue with the package mentioned above, not pip.
hint: See above for details.

NameError: name fcntl is not defined on Windows 10 with Python 3.8

TL;TR: Does jpterm support Windows?

I tried it on Windows 10 with built-in installation of Python inside PowerShell console and it failed:

❯ python -c 'import jpterm; print(jpterm.__version__)'
0.2.1
❯ python
Python 3.8.6 (tags/v3.8.6:db45529, Sep 23 2020, 15:52:53) [MSC v.1927 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import jpterm
>>> jpterm.main()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\mateuszl\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\jpterm.py", line 226, in main
    screen = urwid.raw_display.Screen()
  File "C:\Users\mateuszl\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\urwid\raw_display.py", line 85, in __init__
    fcntl.fcntl(self._resize_pipe_rd, fcntl.F_SETFL, os.O_NONBLOCK)
NameError: name 'fcntl' is not defined
❯ az vm list --output json | python -c 'import jpterm; jpterm.main()'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Users\mateuszl\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\jpterm.py", line 221, in main
    input_json = _load_input_json(getattr(args, 'input-json', None))
  File "C:\Users\mateuszl\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\jpterm.py", line 189, in _load_input_json
    sys.stdin = open(os.ctermid(), 'r')
AttributeError: module 'os' has no attribute 'ctermid'

Change window focus with a keyboard

There's an option to click into the Input JSON window or JMESPath Result window with a mouse so you can then scroll inside them. However, I cannot find a way to select the windows with a keyboard.
Also, the scrolling inside the windows only works with the arrow keys so it's weird that you cannot select the windows without the mouse.

ImportError: cannot import name 'getargspec' from 'inspect'

Traceback (most recent call last):
  File "/usr/local/bin//jpterm", line 2, in <module>
    import jpterm
  File "/usr/local/lib/python3.11/site-packages/jpterm.py", line 7, in <module>
    import urwid
  File "/usr/local/lib/python3.11/site-packages/urwid/__init__.py", line 24, in <module>
    from urwid.widget import (FLOW, BOX, FIXED, LEFT, RIGHT, CENTER, TOP, MIDDLE,
  File "/usr/local/lib/python3.11/site-packages/urwid/widget.py", line 33, in <module>
    from urwid.split_repr import split_repr, remove_defaults, python3_repr
  File "/usr/local/lib/python3.11/site-packages/urwid/split_repr.py", line 22, in <module>
    from inspect import getargspec
ImportError: cannot import name 'getargspec' from 'inspect' (/usr/local/Cellar/[email protected]/3.11.3/Frameworks/Python.framework/Versions/3.11/lib/python3.11/inspect.py)
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
BrokenPipeError: [Errno 32] Broken pipe

env:

os : mac
python: Python 3.11.3
other: Pygments-2.15.1 jmespath-1.0.0 jmespath-terminal-0.2.1 urwid-1.2.2

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.