gvx / strictyaml

Type-safe YAML parser.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

StrictYAML

StrictYAML is a type-safe YAML parser built atop ruamel.yaml that parses a restricted subset of the YAML specificaton.

Priorities:

  • Readability of YAML.
  • Ease of use of API.
  • Secure by default.
  • Strict validation of markup and straightforward type casting.
  • Clear, human readable exceptions with line numbers.
  • Acting as a drop in replacement for pyyaml, ruamel.yaml or poyo.
  • Letting you worry about more interesting things than parsing config files.

Simple example:

name: Ford Prefect
age: 42
posessions:
  - Towel

Default parse result:

>>> strictyaml.load(yaml) \
  == {"name": "Ford Prefect", "age": "42", "possessions": ["Towel", ]}   # All data is str, list or dict

Example using optional validator - using mapping, sequence, string and integer:

>>> from strictyaml import load, Map, Str, Int, Seq
>>> load(yaml, Map({"name": Str(), "age": Int(), "possessions": Seq(Str())})) \
  == {"name": "Ford Prefect", "age": 42, "possessions": ["Towel", ]}     # 42 is now an int

Install It

$ pip install strictyaml

FAQ

From learning programmers:

If you're looking at this and thinking "why not do/use X instead?" that's a healthy response, and you deserve answers. These are probably the questions you're asking:

Map Patterns

If you're not sure what the key name is going to be in a map but you know what type the keys and values will be, use "MapPattern".

emails:
  arthur: arthur@earth.gov
  zaphod: zaphod@beeblebrox.com
  ford: ford@megadodo-publications.com
>>> from strictyaml import load, Map, MapPattern, Str
>>> load(yaml, Map({"emails": MapPattern({Str(), Str()})}) \
  == {"emails": {"arthur": "arthur@earth.gov", "zaphod": "zaphod@beeblebrox.com", "ford": "ford@megadodo-publications.com"}}

Optional values

If you want to use a mapping with a number of required keys and a number of optional keys use "Optional":

arthur:
  email: arthur@earth.gov
zaphod:
  email: zaphod@beeblebrox.com
  job: President of the Galaxy
ford:
  email: ford@ursa-minor.com
  job: Freelance "journalist"

This would be parsed like so:

>>> from strictyaml import load, MapPattern, Map, Str, Optional
>>> load(yaml, MapPattern(Str(), Map({"email": Str(), Optional("job"): Str()}))) \
  == {
         "arthur": {'email': 'arthur@earth.gov',},
         "zaphod": {'email': 'zaphod@beeblebrox.com', 'job': 'President of the Galaxy'},
         "ford": {'email': 'ford@ursa-minor.com', 'job': 'Freelance "journalist"'},
     }

Either/Or

If, for example, you want to parse something as a list of strings or an individual string, you can use a pipe operator to distinguish between them - like so: |

zaphod:
  email: zaphod@beeblebrox.com
  victims: Good taste
ford:
  email: ford@ursa-minor.com
  victims: Journalistic integrity
arthur:
  email: arthur@earth.gov
  victims:
    - A bowl of petunias
    - Agrajag
    - A sperm whale

This would be parsed like so:

>>> from strictyaml import load, Seq, Map, Str, Optional
>>> load(yaml, MapPattern(Str(), Map({"email": Str(), "victims": Str() | Seq(Str())}))) \
  == {
         "zaphod": {'email': 'zaphod@beeblebrox.com', 'victims': 'President of the Galaxy'},
         "arthur": {'email': 'arthur@earth.gov', 'victims': 'Journalistic integrity'},
         "ford": {'email': 'ford@ursa-minor.com', 'victims': ['A bowl of petunias', 'Agrajag', 'A sperm whale', ]},
     }

Numbers

StrictYAML will parse a string into integers, floating point or decimal (non-floating point) numbers if you specify it:

>>> import from strictyaml import load, Map
>>> load("int: 42", Map({"int": strictyaml.Int()})) == {"int": 42}
>>> load("float: 42.3333", Map({"float": strictyaml.Float()})) == {"float": 42.3333}
>>> load("price: 35.42811", Map({"price": strictyaml.Decimal()})) == {"price": decimal.Decimal('35.42811')}

Booleans

Upper case or lower case - it doesn't matter. Yes, on and true are treated as True and no, off and false are treated as False.

>>> load("booltrue: yes", Map({"booltrue": strictyaml.Bool()})) == {"booltrue": True}
>>> load("boolfalse: no", Map({"boolfalse": strictyaml.Bool()})) == {"booltrue": True}
>>> load("booltrue: true", Map({"booltrue": strictyaml.Bool()})) == {"booltrue": True}
>>> load("boolfalse: False", Map({"boolfalse": strictyaml.Bool()})) == {"booltrue": False}

Empty values

Empty values can be be validated and returned as None, {} and []:

>>> load("empty: ", Map({"empty": strictyaml.EmptyNone()})) == {"empty": None}
>>> load("empty: ", Map({"empty": strictyaml.EmptyDict()})) == {"empty": {}}
>>> load("empty: ", Map({"empty": strictyaml.EmptyList()})) == {"empty": []}

Enums

>>> load("day: monday", Map({"day": strictyaml.Enum(["monday", "tuesday", "wednesday"])})) == {"day": "monday"}

Datetimes

This datetime parser uses dateutil's datetime parser, which is used, among others, by arrow, delorean and pendulum.

>>> load("date: 2016-10-22T14:23:12+00:00", Map({"date": strictyaml.Datetime()})) == {"date": datetime(2016, 10, 22, 14, 23, 12)}

Custom scalar types

COMING SOON

Using YAML Valdation

See: What is kwalify and when should I use it?

COMING SOON

Roundtripping YAML

COMING SOON

Contributors

  • @gvx
  • @ AlexandreDecan
  • @lots0logs

About

Type-safe YAML parser.

License:MIT License


Languages

Language:Python 100.0%