Skip to content

Commit

Permalink
Merge pull request #42 from dev-cafe/multiple-trees
Browse files Browse the repository at this point in the history
Refactor validation
  • Loading branch information
robertodr committed Mar 24, 2019
2 parents 2e05b5d + 4a2c7e5 commit 08a6035
Show file tree
Hide file tree
Showing 45 changed files with 2,973 additions and 585 deletions.
28 changes: 12 additions & 16 deletions .azure-pipelines.yml
Original file line number Diff line number Diff line change
@@ -1,35 +1,31 @@
jobs:

- job: 'Windows'
- job: 'macOS'
pool:
vmImage: 'vs2017-win2016'
vmImage: 'macOS-10.13'
strategy:
matrix:
Python37:
python.version: '3.7'
Python36:
python.version: '3.6'
Python35:
python.version: '3.5'
Python34:
python.version: '3.4'
Python36:
python.version: '3.6'
Python37:
python.version: '3.7'
maxParallel: 2
steps:
- template: .ci/azure-steps.yml

- job: 'macOS'
- job: 'Windows'
pool:
vmImage: 'macOS-10.13'
vmImage: 'vs2017-win2016'
strategy:
matrix:
Python37:
python.version: '3.7'
Python36:
python.version: '3.6'
Python35:
python.version: '3.5'
Python34:
python.version: '3.4'
Python36:
python.version: '3.6'
Python37:
python.version: '3.7'
maxParallel: 2
steps:
- template: .ci/azure-steps.yml
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ instance/

# Sphinx documentation
docs/_build/
# These are autogenerated
docs/modules.rst
docs/parselglossy*.rst

# PyBuilder
target/
Expand Down Expand Up @@ -114,3 +117,5 @@ GPATH
GRTAGS
GTAGS

# Validated input files produced during testing
validated*.json
4 changes: 0 additions & 4 deletions .style.yapf

This file was deleted.

6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ dist: xenial
language: python

python:
- 3.7
- 3.6
- 3.5
- 3.4
- 3.6
- 3.7
- 3.8-dev

env:
- PIPENV_VERBOSITY=-1
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ Before you submit a pull request, check that it meets these guidelines:
3. If the pull request adds functionality, the docs should be updated. Put
your new functionality into a function with a docstring, and add the
feature to the list in ``README.rst``.
4. The pull request should work for Python 3.4, 3.5, 3.6, and 3.7. Check
4. The pull request should work for Python 3.5, 3.6, and 3.7. Check
https://travis-ci.org/dev-cafe/parselglossy/pull_requests
and make sure that the tests pass for all supported Python versions.

Expand Down
29 changes: 25 additions & 4 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,36 @@
History
=======

0.2.0 (2019-03-11)
------------------
Unreleased
----------

* Dropped support for Python 3.4.
* Renamed the ``section`` and ``keyword`` fields in the template to ``name``.
* Renamed the ``documentation`` field in the template to ``docstring``.
* Arbitrary callables of the input dictionary are now allowed in the ``default``
field. Fix `#31 <https://github.com/dev-cafe/parselglossy/issues/31>`_.
* Complex numbers have the proper type after reading in. Fix `#26 <https://github.com/dev-cafe/parselglossy/issues/26>`_.
* Better error reporting. Exceptions are raised after each validation stage and
offer detail error messages that give a comprehensive overview of what went
wrong. Fix `#24 <https://github.com/dev-cafe/parselglossy/issues/24>`_.
* Fully defaulted sections are now properly taken into account. Fix `#33
<https://github.com/dev-cafe/parselglossy/issues/33>`_.
* Nesting of sections under keywords will throw an exception. Fix `#34
<https://github.com/dev-cafe/parselglossy/issues/34>`_.

0.2.0_ (2019-03-11)
-------------------

* Implementation of the Getkw_ input grammar.
* Implementation of the validation infrastructure.

0.1.0 (2018-12-03)
------------------
0.1.0_ (2018-12-03)
-------------------

* First release on PyPI.


.. _Unreleased: https://github.com/dev-cafe/parselglossy/compare/v0.2.0...HEAD
.. _0.2.0: https://github.com/dev-cafe/parselglossy/releases/tag/v0.2.0
.. _0.1.0: https://pypi.org/project/parselglossy/0.1.0/
.. _Getkw: https://github.com/dev-cafe/libgetkw
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Generic input parsing library, speaking in tongues
Requirements
------------

* Python 3.4 or later.
* Python 3.5 or later.


Features
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import sys

import guzzle_sphinx_theme
from sphinx.apidoc import main
from sphinx.ext.apidoc import main

sys.path.insert(0, str(pathlib.Path(__file__).parents[1]))

Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Generic input parsing library, speaking in tongues.
contributing
authors
history
modules

Indices and tables
==================
Expand Down
35 changes: 35 additions & 0 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,38 @@ Usage
To use parselglossy in a project::

import parselglossy


Validation specifications
---------------------------

Possible fields for keywords are:

:`name`:
The name of the keyword. Mandatory.
:`type`:
Type of the corresponding value. Mandatory.
:`docstring`:
A documentation string, possibly multiline. Mandatory.
:`default`:
The default value. It can be a value of the declared type or a callable.
The callable can be used to compute default values based on the default
value of any other keyword in the input. If absent, the keyword is
required, meaning that the user will have to supply it in their own
input.
:`predicates`:
A list of callables to sanity-check the passed value. Note that type checking is
always performed automatically.

Possible fields for sections are:

:`name`:
The name of the section. Mandatory.
:`docstring`:
A documentation string, possibly multiline. Mandatory.
:`keywords`:
A list of keywords belonging to the section.
:`sections`:
A list of sections belonging to the section.

One or both of `keywords` and `sections` **must** be present.
150 changes: 150 additions & 0 deletions parselglossy/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#
# parselglossy -- Generic input parsing library, speaking in tongues
# Copyright (C) 2019 Roberto Di Remigio, Radovan Bast, and contributors.
#
# This file is part of parselglossy.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# For information on the complete list of contributors to the
# parselglossy library, see: <http://parselglossy.readthedocs.io/>
#

# -*- coding: utf-8 -*-
"""Top-level functions for parselglossy."""

import json
from pathlib import Path
from typing import Optional, Union

from .grammars import lexer
from .utils import JSONDict, as_complex, path_resolver, read_yaml_file
from .validation import validate_from_dicts


def lex(
*,
infile: Union[str, Path],
grammar: str,
ir_file: Optional[Union[str, Path]] = None
) -> JSONDict:
"""Run grammar of choice on input string.
Parameters
----------
in_str : IO[Any]
The string to be parsed.
grammar : str
Grammar to be used.
ir_file : Union[str, Path]
File to write intermediate representation to (JSON format).
None by default, which means file is not written out.
Returns
-------
The contents of the input string as a dictionary.
"""

infile = path_resolver(infile)
if ir_file is not None:
ir_file = path_resolver(ir_file)

with infile.open("r") as f:
ir = lexer.lex_from_str(in_str=f, grammar=grammar, ir_file=ir_file)

return ir


def validate(
*,
infile: Union[str, Path],
fr_file: Optional[Union[str, Path]] = None,
template: Union[str, Path]
) -> JSONDict:
"""Validate intermediate representation into final representation.
Parameters
----------
infile : Union[str, Path]
The file with the intermediate representation (JSON format).
fr_file : Union[str, Path]
File to write final representation to (JSON format).
None by default, which means file is not written out.
template : Union[str, Path]
Which validation template to use.
"""

infile = path_resolver(infile)
with infile.open("r") as f:
ir = json.load(f, object_hook=as_complex)

template = path_resolver(template)
stencil = read_yaml_file(Path(template))

if fr_file is not None:
fr_file = path_resolver(fr_file)

fr = validate_from_dicts(ir=ir, template=stencil, fr_file=fr_file)

return fr


def parse(
*,
infile: Union[str, Path],
outfile: Optional[Union[str, Path]] = None,
grammar: str,
template: Union[str, Path],
dump_ir: bool = False
) -> None:
"""Parse input file.
Parameters
----------
infile : Union[str, Path]
The input file to be parsed.
outfile : Union[str, Path]
The output file.
None by default, which means file name default to <infile>_fr.json
grammar : str
Which grammar to use.
template : Union[str, Path]
Which validation template to use.
write_ir_out : bool
Whether to write out the intermediate representation to file (JSON format).
False by default. If true the filename if <infile>_ir.json
"""

stem = infile.rsplit(".", 1)[0] if isinstance(infile, str) else infile.stem

infile = path_resolver(infile)
if dump_ir:
ir_file = path_resolver(stem + "_ir.json")
else:
ir_file = None

ir = lex(infile=infile, ir_file=ir_file, grammar=grammar)

template = path_resolver(template)
stencil = read_yaml_file(Path(template))

if outfile is not None:
outfile = path_resolver(outfile)

fr = validate_from_dicts(ir=ir, template=stencil, fr_file=outfile) # noqa: F841
Loading

0 comments on commit 08a6035

Please sign in to comment.