jqfpyでyamlを実験的にサポートしてみた

github.com

jqfpyでyamlを実験的にサポートしてみた。実験的という言葉がついている理由は完全にサポートしているというわけじゃないということ。 具体的には、連続したjsonに対応した入力をサポートしていない(後で詳しく)。

yaml対応版のインストール

yaml対応版のインストールのインストールは以下の様にする。

$ pip install 'jqfpy[yaml]'

動作例

PyYAMLがインストールしていれば普通に動く。ファイルの拡張子を見て良い感じに扱ってくれる。特にフォーマットを指定していない場合にはjsonで出力される。

00data.yaml

$ jqfpy 00data.yaml
{
  "person": {
    "name": "foo",
    "age": 20,
    "skills": [
      "x",
      "y",
      "z"
    ]
  }
}

$ jqfpy -r '"{p[name]}({p[age]})".format(p=get("person"))' 00data.yaml
foo(20)

入っていない場合には以下の様なエラーが出る。

$ jqfpy 00data.yaml
yaml module is not found. please install via 
  pip install 'jqfpy[yaml]'

フォーマットの指定

-o, --output-formatyamlを指定すると出力もyamlになる。

$ jqfpy -o yaml 00data.yaml
person:
  name: foo
  age: 20
  skills:
  - x
  - y
  - z

$ jqfpy -c -o yaml 00data.yaml
{person: {name: foo, age: 20, skills: [x, y, z]}}

catからパイプで繋げられたときなど、ファイルのフォーマットがファイル名から取得できない場合もある。そのような場合には、-i, --input-format を使う。

$ cat 00data.yaml | jqfpy -r -i yaml 'get("person/name")'
foo

連続したデータ

残念ながら完全なサポートではなく連続したデータなどに対応していない。 例えば、jsonで言う以下の様な出力を例に取る。

01data.json

{
  "person": {
    "name": "foo",
    "age": 20,
    "skills": [
      "x",
      "y",
      "z"
    ]
  }
}
{
  "person": {
    "name": "bar",
    "age": 10,
    "skills": []
  }
}

これをそのままyamlに直すと以下の様になる。

01data.yaml

person:
  name: foo
  age: 20
  skills:
    - x
    - y
    - z
person:
  name: bar
  age: 10
  skills: []

このyamlが2つのデータになれば良いはずなのだけれど。yamlのパーサーの問題でちょっと対応方法が思いつかなかった。現在の所は1つだけのデータと認識されてしまう。

$ jqfpy 01data.yaml
{
  "person": {
    "name": "bar",
    "age": 10,
    "skills": []
  }`
}

ちょっとだけあがいてみていて、yamlの仕様的に---がドキュメントの区切りとして定義されているので出力のときにはこれを利用するようにしている。こちらは2つのデータとして認識される。

$ jqfpy -o yaml 01data.json
person:
  age: 20
  name: foo
  skills:
  - x
  - y
  - z
---
person:
  age: 10
  name: bar
  skills: []

$ jqfpy -o yaml 01data.json | jqfpy -i yaml -c --force-stdin
{"person": {"age": 20, "name": "foo", "skills": ["x", "y", "z"]}}
{"person": {"age": 10, "name": "bar", "skills": []}}

ちょっとだけ困ったこと

ちょっとだけ困ったことが実は存在していて。標準入力から入力がなければヘルプを表示と言うようにしているのだけれど。パイプで繋いでいった状態でちょっと処理が重たいときなどには誤動作してしまうことがある。ヘルプを表示してしまう。

これに対して標準入力を見ることを強制するために --force-stdin というオプションを設けている。これがちょっと微妙。

ちなみに標準入力の有無はselectでみている。このあたりの待機時間を調整すれば良いのかもという話ではあるのだけれど。それもそれで微妙な感じ。

def is_fd_alive(fd):
    if os.name == 'nt':
        return not os.isatty(fd.fileno())
    import select
    return bool(select.select([fd], [], [], 0.07)[0])

追記:

以下が動かないのはちょっとありえないので。ヘルプメッセージを表示する機能消した(0.3.2)。

(sleep 1; cat 01data.json ) | jqfpy

ヘルプメッセージが見たい場合には明示的に jqfpy -h と入力する。