Skip to content

Commit

Permalink
Add more specialized exceptions
Browse files Browse the repository at this point in the history
Resolves #198
  • Loading branch information
goodmami committed Apr 18, 2019
1 parent 783327f commit 7553c45
Show file tree
Hide file tree
Showing 10 changed files with 161 additions and 49 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ these changes are prefixed with "**BREAKING**"
- `STRING_TYPE`
- `SemI.find_synopsis()`
- `SemIError`
- `SemISyntaxError`
- `SemIWarning`
- `Synopsis`
- `SynopsisRole`
Expand All @@ -45,6 +46,7 @@ these changes are prefixed with "**BREAKING**"
- `EVENTUALITY`
- `INSTANCE`
- `is_valid()`
* `delphin.mrs.vpm.VPMSyntaxError`

### Moved or Renamed

Expand All @@ -55,6 +57,7 @@ these changes are prefixed with "**BREAKING**"
* `delphin.exceptions.TdlWarning` to `delphin.tdl.TDLWarning`
* `delphin.exceptions.TSQLSyntaxError` to `delphin.tsql.TSQLSyntaxError`
* `delphin.extra.highlight.TdlLexer` to `delphin.extra.highlight.TDLLexer`
* `delphin.interfaces.ace.AceProcessError` to `delphin.interfaces.ace.ACEProcessError`
* `delphin.mrs.util.rargname_sortkey()` to `delphin.sembase.role_priority()`
* `delphin.mrs.components.Lnk` to `delphin.lnk.Lnk`
* `delphin.mrs.components._LnkMixin` to `delphin.lnk.LnkMixin`
Expand Down Expand Up @@ -115,12 +118,17 @@ these changes are prefixed with "**BREAKING**"
### Changed

* `delphin.extra.highlight` add docstrings and wild-card to TDL highlighter
* `delphin.interfaces.ace` no longer raise `ValueError` when grammar file does
not exist; the `ACEProcessError` now handles this
* `delphin.semi.load()` takes an `encoding` parameter
* `delphin.semi` dictionary schema removes empty/default values; changes
structure of predicate synopses
* `delphin.semi` warns when a type is redefined
* `delphin.tfs` -- errors raise `TypeHierarchyError` instead of `ValueError`
* `delphin.tfs` nodes in the hierarchy are now TypeHierarchyNodes and may
contain arbitrary data in addition to the parents and children
* `delphin.mrs.vpm` raises `VPMSyntaxError` on bad inputs instead of
`AssertionError`


## [v0.9.2][]
Expand Down
6 changes: 2 additions & 4 deletions delphin/interfaces/ace.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
from delphin.exceptions import PyDelphinException


class AceProcessError(PyDelphinException):
class ACEProcessError(PyDelphinException):
"""Raised when the ACE process has crashed and cannot be recovered."""


Expand Down Expand Up @@ -120,8 +120,6 @@ class AceProcess(Processor):

def __init__(self, grm, cmdargs=None, executable=None, env=None,
tsdbinfo=True, **kwargs):
if not os.path.isfile(grm):
raise ValueError("Grammar file %s does not exist." % grm)
self.grm = grm

self.cmdargs = cmdargs or []
Expand Down Expand Up @@ -172,7 +170,7 @@ def _open(self):
'start': datetime.now()
})
if self._p.poll() is not None and self._p.returncode != 0:
raise AceProcessError('Process closed on startup; see <stderr>.')
raise ACEProcessError('Process closed on startup; see <stderr>.')

def __enter__(self):
return self
Expand Down
32 changes: 27 additions & 5 deletions delphin/mrs/vpm.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@

import re

from delphin.exceptions import PyDelphinSyntaxError
from delphin import variable

_LR_OPS = set(['<>', '>>', '==', '=>'])
_RL_OPS = set(['<>', '<<', '==', '<='])
_SUBSUME_OPS = set(['<>', '<<', '>>'])
_EQUAL_OPS = set(['==', '<=', '=>'])
_ALL_OPS = _LR_OPS.union(_RL_OPS)


class VPMSyntaxError(PyDelphinSyntaxError):
"""Raised when loading an invalid VPM."""


def load(source, semi=None):
Expand All @@ -41,11 +47,12 @@ def load(source, semi=None):
return _load(fh, semi)

def _load(fh, semi):
filename = getattr(fh, 'name', '<stream>')
typemap = []
propmap = []
curmap = typemap
lh, rh = 1, 1 # number of elements expected on each side
for line in fh:
for lineno, line in enumerate(fh, 1):
line = line.lstrip()

if not line or line.startswith(';'):
Expand All @@ -63,16 +70,31 @@ def _load(fh, semi):
match = re.match(r'(?P<lvals>.*)(?P<op>[<>=]{2})(?P<rvals>.*)$', line)
if match is not None:
lvals = match.group('lvals').split()
op = match.group('op')
rvals = match.group('rvals').split()
assert len(lvals) == lh
assert len(rvals) == rh
curmap.append((lvals, match.group('op'), rvals))
msg, offset = '', -1
if len(lvals) != lh:
msg = 'wrong number of values on left side'
offset = match.end('lvals')
if op not in _ALL_OPS:
msg = 'invalid operator'
offset = match.end('op')
elif len(rvals) != rh:
msg = 'wrong number of values on right side'
offset = match.end('rvals')
if msg:
raise VPMSyntaxError(msg, filename=filename,
lineno=lineno, offset=offset,
text=line)
curmap.append((lvals, op, rvals))
continue

raise ValueError('Invalid line in VPM file: {}'.format(line))
raise VPMSyntaxError('invalid line in VPM file',
filename=filename, lineno=lineno, text=line)

return VPM(typemap, propmap, semi)


class VPM(object):
"""
A variable-property mapping.
Expand Down
83 changes: 53 additions & 30 deletions delphin/semi.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from delphin import tfs
from delphin.exceptions import (
PyDelphinException,
PyDelphinSyntaxError,
PyDelphinWarning
)

Expand Down Expand Up @@ -78,15 +79,15 @@


class SemIError(PyDelphinException):
"""
Raised when loading an invalid SEM-I.
"""
"""Raised when loading an invalid SEM-I."""


class SemISyntaxError(PyDelphinSyntaxError):
"""Raised when loading an invalid SEM-I."""


class SemIWarning(PyDelphinWarning):
"""
Warning class for questionable SEM-Is.
"""
"""Warning class for questionable SEM-Is."""


def load(fn, encoding='utf-8'):
Expand All @@ -107,14 +108,14 @@ def load(fn, encoding='utf-8'):

def _read_file(fn, basedir, encoding):
data = {
'variables': [],
'properties': [],
'roles': [],
'variables': {},
'properties': {},
'roles': {},
'predicates': {},
}
section = None

for line in open(fn, 'r', encoding=encoding):
for lineno, line in enumerate(open(fn, 'r', encoding=encoding), 1):
line = line.lstrip()

if not line or line.startswith(';'):
Expand All @@ -124,7 +125,9 @@ def _read_file(fn, basedir, encoding):
if match is not None:
name = match.group('name')
if name not in _SEMI_SECTIONS:
raise ValueError('Invalid SEM-I section: {}'.format(name))
raise SemISyntaxError(
'invalid SEM-I section',
filename=fn, lineno=lineno, text=line)
else:
section = name
continue
Expand All @@ -134,9 +137,12 @@ def _read_file(fn, basedir, encoding):
include_fn = pjoin(basedir, match.group('filename').rstrip())
include_data = _read_file(
include_fn, dirname(include_fn), encoding)
data['variables'].extend(include_data.get('variables', []))
data['properties'].extend(include_data.get('properties', []))
data['roles'].extend(include_data.get('roles', []))
for key, val in include_data['variables'].items():
_incorporate(data['variables'], key, val, include_fn)
for key, val in include_data['properties'].items():
_incorporate(data['properties'], key, val, include_fn)
for key, val in include_data['roles'].items():
_incorporate(data['roles'], key, val, include_fn)
for pred, d in include_data['predicates'].items():
if pred not in data['predicates']:
data['predicates'][pred] = {
Expand All @@ -148,7 +154,7 @@ def _read_file(fn, basedir, encoding):
if d.get('synopses'):
data['predicates'][pred]['synopses'].extend(d['synopses'])

if section == 'variables':
elif section == 'variables':
# e.g. e < i : PERF bool, TENSE tense.
match = _variable_entry_re.match(line)
if match is not None:
Expand All @@ -162,9 +168,11 @@ def _read_file(fn, basedir, encoding):
properties = [pair.split() for pair in pairs]
v = {'parents': supertypes, 'properties': properties}
# v = type(identifier, supertypes, d)
data['variables'].append((identifier, v))
_incorporate(data['variables'], identifier, v, fn)
else:
raise ValueError('Invalid entry: {}'.format(line))
raise SemISyntaxError(
'invalid variable',
filename=fn, lineno=lineno, text=line)

elif section == 'properties':
# e.g. + < bool.
Expand All @@ -174,18 +182,23 @@ def _read_file(fn, basedir, encoding):
supertypes = match.group('parents') or []
if supertypes:
supertypes = supertypes.split(' & ')
data['properties'].append((_type, {'parents': supertypes}))
_incorporate(
data['properties'], _type, {'parents': supertypes}, fn)
else:
raise ValueError('Invalid entry: {}'.format(line))
raise SemISyntaxError(
'invalid property',
filename=fn, lineno=lineno, text=line)

elif section == 'roles':
# e.g. + < bool.
match = _role_entry_re.match(line)
if match is not None:
rargname, value = match.group('role'), match.group('value')
data['roles'].append((rargname, {'value': value}))
role, value = match.group('role'), match.group('value')
_incorporate(data['roles'], role, {'value': value}, fn)
else:
raise ValueError('Invalid entry: {}'.format(line))
raise SemISyntaxError(
'invalid role',
filename=fn, lineno=lineno, text=line)

elif section == 'predicates':
# e.g. _predicate_n_1 : ARG0 x { IND + }.
Expand Down Expand Up @@ -217,6 +230,12 @@ def _read_file(fn, basedir, encoding):
return data


def _incorporate(d, key, val, fn):
if key in d:
warnings.warn("'{}' redefined in {}".format(key, fn), SemIWarning)
d[key] = val


class SynopsisRole(tuple):
"""
Role data associated with a SEM-I predicate synopsis.
Expand Down Expand Up @@ -358,22 +377,26 @@ def __init__(self,
self.roles = {}
self.predicates = tfs.TypeHierarchy(TOP_TYPE)
# validate and normalize inputs
self._init_properties(properties)
self._init_variables(variables)
self._init_roles(roles)
self._init_predicates(predicates)
if properties:
self._init_properties(properties)
if variables:
self._init_variables(variables)
if roles:
self._init_roles(roles)
if predicates:
self._init_predicates(predicates)

def _init_properties(self, properties):
subhier = {}
for prop, data in dict(properties or []).items():
for prop, data in properties.items():
prop = prop.lower()
parents = data.get('parents')
_add_to_subhierarchy(subhier, prop, parents, None)
self.properties.update(subhier)

def _init_variables(self, variables):
subhier = {}
for var, data in dict(variables or []).items():
for var, data in variables.items():
var = var.lower()
parents = data.get('parents')
properties = []
Expand All @@ -386,7 +409,7 @@ def _init_variables(self, variables):
self.variables.update(subhier)

def _init_roles(self, roles):
for role, data in dict(roles or []).items():
for role, data in roles.items():
role = role.upper()
var = data['value'].lower()
if not (var == STRING_TYPE or var in self.variables):
Expand All @@ -396,7 +419,7 @@ def _init_roles(self, roles):
def _init_predicates(self, predicates):
subhier = {}
propcache = {v: dict(node.data) for v, node in self.variables.items()}
for pred, data in dict(predicates or []).items():
for pred, data in predicates.items():
pred = pred.lower()
parents = data.get('parents')
synopses = []
Expand Down
2 changes: 1 addition & 1 deletion docs/api/delphin.interfaces.ace.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,5 @@ delphin.interfaces.ace
Exceptions
----------

.. autoexception:: AceProcessError
.. autoexception:: ACEProcessError
:show-inheritance:
19 changes: 16 additions & 3 deletions docs/api/delphin.mrs.vpm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ delphin.mrs.vpm

.. automodule:: delphin.mrs.vpm

.. autofunction:: load
.. autoclass:: VPM
:members:
Module functions
----------------

.. autofunction:: load

Classes
-------

.. autoclass:: VPM
:members:

Exceptions
----------

.. autoexception:: VPMSyntaxError
:show-inheritance:
10 changes: 8 additions & 2 deletions docs/api/delphin.semi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,14 @@ delphin.semi

.. autoclass:: SynopsisRole

Exceptions
----------
Exceptions and Warnings
-----------------------

.. autoexception:: SemIError
:show-inheritance:

.. autoexception:: SemISyntaxError
:show-inheritance:

.. autoexception:: SemIWarning
:show-inheritance:
4 changes: 2 additions & 2 deletions tests/interfaces_ace_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def test_start(ace_mismatch, tmpdir, monkeypatch):
with monkeypatch.context() as m:
m.setattr(ace, 'Popen', popen)
m.setattr(ace, '_ace_version', lambda x: (0, 9, 29))
with pytest.raises(ace.AceProcessError):
with pytest.raises(ace.ACEProcessError):
ace.AceParser(str(grm))
with pytest.raises(ace.AceProcessError):
with pytest.raises(ace.ACEProcessError):
ace.parse(str(grm), 'Dogs sleep.')
Loading

0 comments on commit 7553c45

Please sign in to comment.