Skip to main content

PyYAML-based module to produce a bit more pretty and readable YAML-serialized data

Project description

PyYAML-based python module to produce a bit more pretty and human-readable YAML-serialized data.

This module is for serialization only, see ruamel.yaml module for literate YAML parsing (keeping track of comments, spacing, line/column numbers of values, etc).

(side-note: to dump stuff parsed by ruamel.yaml with this module, use only YAML(typ='safe') there)

It’s a small module, and for projects that only need part of its functionality, I’d recommend copy-pasting that in, instead of adding janky dependency.

Repository URLs:

Warning

Prime goal of this module is to produce human-readable output that can be easily diff’ed, manipulated and re-used, but maybe with occasional issues.

So please do not rely on the thing to produce output that can always be deserialized exactly to what was exported, at least - use PyYAML directly for that (but maybe with options from the next section).

What this module does and why

YAML is generally nice and easy format to read if it was written by humans.

PyYAML can a do fairly decent job of making stuff readable, and the best combination of parameters for such output that I’ve seen so far is probably this one:

>>> m = [123, 45.67, {1: None, 2: False}, 'some text']
>>> data = dict(a='asldnsa\nasldpáknsa\n', b='whatever text', ma=m, mb=m)
>>> yaml.safe_dump(data, sys.stdout, allow_unicode=True, default_flow_style=False)
a: 'asldnsa

  asldpáknsa

  '
b: whatever text
ma: &id001
- 123
- 45.67
- 1: null
  2: false
- some text
mb: *id001

pyaml (this module) tries to improve on that a bit, with the following tweaks:

  • Most human-friendly representation options in PyYAML (that I know of) are used as defaults.

  • Dump “null” values as empty values, if possible, which have the same meaning but reduce visual clutter and are easier to edit.

  • Dicts, sets, OrderedDicts, defaultdicts, namedtuples, enums, dataclasses, etc are represented as their safe YAML-compatible base (like int, list or mapping), with mappings key-sorted by default for more diff-friendly output.

  • Use shorter and simplier yes/no for booleans.

  • List items get indented, as they should be.

  • Attempt is made to pick more readable string representation styles, depending on the value, e.g.:

    >>> yaml.safe_dump(cert, sys.stdout)
    cert: '-----BEGIN CERTIFICATE-----
    
      MIIH3jCCBcagAwIBAgIJAJi7AjQ4Z87OMA0GCSqGSIb3DQEBCwUAMIHBMRcwFQYD
    
      VQQKFA52YWxlcm9uLm5vX2lzcDEeMBwGA1UECxMVQ2VydGlmaWNhdGUgQXV0aG9y
    ...
    
    >>> pyaml.p(cert):
    cert: |
      -----BEGIN CERTIFICATE-----
      MIIH3jCCBcagAwIBAgIJAJi7AjQ4Z87OMA0GCSqGSIb3DQEBCwUAMIHBMRcwFQYD
      VQQKFA52YWxlcm9uLm5vX2lzcDEeMBwGA1UECxMVQ2VydGlmaWNhdGUgQXV0aG9y
    ...
  • “force_embed” option (default=yes) to avoid having &id stuff scattered all over the output. Might be more useful to disable it in some specific cases though.

  • “&id” anchors, if used, get labels from the keys they get attached to, not just meaningless enumerators.

  • “string_val_style” option to only apply to strings that are values, not keys, i.e:

    >>> pyaml.p(data, string_val_style='"')
    key: "value\nasldpáknsa\n"
    >>> yaml.safe_dump(data, sys.stdout, allow_unicode=True, default_style='"')
    "key": "value\nasldpáknsa\n"
  • Add vertical spacing (empty lines) between keys on different depths, to separate long YAML sections in the output visually, make it more seekable.

  • Discard end-of-document “…” indicators for simple values.

Result for the (rather meaningless) example above:

>>> pyaml.p(data, force_embed=False, vspacing=dict(split_lines=10))

a: |
  asldnsa
  asldpáknsa

b: whatever text

ma: &ma
  - 123
  - 45.67
  - 1:
    2: no
  - some text

mb: *ma

(force_embed=False enabled deduplication with &ma anchor, vspacing is adjusted to split even this tiny output)


Extended example:

>>> pyaml.dump(data, vspacing=dict(split_lines=10))

destination:

  encoding:
    xz:
      enabled: yes
      min_size: 5120
      options:
      path_filter:
        - \.(gz|bz2|t[gb]z2?|xz|lzma|7z|zip|rar)$
        - \.(rpm|deb|iso)$
        - \.(jpe?g|gif|png|mov|avi|ogg|mkv|webm|mp[34g]|flv|flac|ape|pdf|djvu)$
        - \.(sqlite3?|fossil|fsl)$
        - \.git/objects/[0-9a-f]+/[0-9a-f]+$

  result:
    append_to_file:
    append_to_lafs_dir:
    print_to_stdout: yes

  url: http://localhost:3456/uri

filter:
  - /(CVS|RCS|SCCS|_darcs|\{arch\})/$
  - /\.(git|hg|bzr|svn|cvs)(/|ignore|attributes|tags)?$
  - /=(RELEASE-ID|meta-update|update)$

http:
  ca_certs_files: /etc/ssl/certs/ca-certificates.crt
  debug_requests: no
  request_pool_options:
    cachedConnectionTimeout: 600
    maxPersistentPerHost: 10
    retryAutomatically: yes

logging:

  formatters:
    basic:
      datefmt: '%Y-%m-%d %H:%M:%S'
      format: '%(asctime)s :: %(name)s :: %(levelname)s: %(message)s'

  handlers:
    console:
      class: logging.StreamHandler
      formatter: basic
      level: custom
      stream: ext://sys.stderr

  loggers:
    twisted:
      handlers:
        - console
      level: 0

  root:
    handlers:
      - console
    level: custom

Note that unless there are many moderately wide and deep trees of data, which are expected to be read and edited by people, it might be preferrable to directly use PyYAML regardless, as it won’t introduce another (rather pointless in that case) dependency and a point of failure.

Some Tricks

  • Pretty-print any yaml or json (yaml subset) file from the shell:

    % python -m pyaml /path/to/some/file.yaml
    % curl -s https://www.githubstatus.com/api/v2/summary.json | python -m pyaml
  • Process and replace json/yaml file in-place:

    % python -m pyaml -r file-with-json.data
  • Easier “debug printf” for more complex data (all funcs below are aliases to same thing):

    pyaml.p(stuff)
    pyaml.pprint(my_data)
    pyaml.pprint('----- HOW DOES THAT BREAKS!?!?', input_data, some_var, more_stuff)
    pyaml.print(data, file=sys.stderr) # needs "from __future__ import print_function"
  • Force all string values to a certain style (see info on these in PyYAML docs):

    pyaml.dump(many_weird_strings, string_val_style='|')
    pyaml.dump(multiline_words, string_val_style='>')
    pyaml.dump(no_want_quotes, string_val_style='plain')

    Using pyaml.add_representer() (note *p*yaml) as suggested in this SO thread (or github-issue-7) should also work.

  • Control indent and width of the results:

    pyaml.dump(wide_and_deep, indent=4, width=120)

    These are actually keywords for PyYAML Emitter (passed to it from Dumper), see more info on these in PyYAML docs.

  • Dump multiple yaml documents into a file: pyaml.dump_all([data1, data2, data3], dst_file)

    explicit_start=True is implied, unless explicit_start=False is passed.

Installation

It’s a regular Python 3.8+ module/package, published on PyPI (as pyaml).

Module uses PyYAML for processing of the actual YAML files and should pull it in as a dependency.

Dependency on unidecode module is optional and should only be necessary with force_embed=False keyword, and same-id objects or recursion is used within serialized data.

Using pip is how you generally install it, usually coupled with venv usage (which will also provide “pip” tool itself):

% pip install pyaml

Current-git version can be installed like this:

% pip install git+https://github.com/mk-fg/pretty-yaml

pip will default to installing into currently-active venv, then user’s home directory (under ~/.local/lib/python...), and maybe system-wide when running as root (only useful in specialized environments like docker containers).

There are many other python packaging tools - pipenv, poetry, pdm, etc - use whatever is most suitable for specific project/environment.

More general info on python packaging can be found at packaging.python.org.

When changing code, unit tests can be run with python -m unittest from the local repository checkout.

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyaml-23.9.2.tar.gz (22.0 kB view details)

Uploaded Source

Built Distribution

pyaml-23.9.2-py3-none-any.whl (19.1 kB view details)

Uploaded Python 3

File details

Details for the file pyaml-23.9.2.tar.gz.

File metadata

  • Download URL: pyaml-23.9.2.tar.gz
  • Upload date:
  • Size: 22.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.3

File hashes

Hashes for pyaml-23.9.2.tar.gz
Algorithm Hash digest
SHA256 eee6dde0c848e126e521e74ae2f21e7a431e4cc3d650b3338696907b237e759c
MD5 0bafbb81c11d386f65b4c9ee45a28c01
BLAKE2b-256 2d153ae32314fb0a5831e6f4c09f4278e6b53bfac2e0987ccae2ebd3135e79ab

See more details on using hashes here.

Provenance

File details

Details for the file pyaml-23.9.2-py3-none-any.whl.

File metadata

  • Download URL: pyaml-23.9.2-py3-none-any.whl
  • Upload date:
  • Size: 19.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.3

File hashes

Hashes for pyaml-23.9.2-py3-none-any.whl
Algorithm Hash digest
SHA256 66dbd8652103866c7f004e2575d97e2d9805ddbfb33588b0d7e30f18bd7c808c
MD5 cc7a1eb6ca04feee2cd86aa851406635
BLAKE2b-256 fb884b7ea32cca1419277260db8a058117e9cdc9ae1fc52b50466940b1ccf544

See more details on using hashes here.

Provenance

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page