Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added exception helper for modified after freeze #881

Merged
merged 17 commits into from
Feb 7, 2019
Merged
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ graft docs
include tox.ini
include setup.py

recursive-include src *.j2
recursive-include docs *
recursive-include misc *
recursive-include tests *
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ tornado==5.1.1
tox==3.7.0
tox-venv==0.3.1
typing==3.6.6
jinja2==2.10
pep8-naming==0.8.2
flake8==3.7.5
flake8==3.7.5
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"typing",
"PyJWT",
"cryptography",
"jinja2",
]

setup(
Expand Down
8 changes: 7 additions & 1 deletion src/inmanta/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ def _get_watched_file_handler(options):

def _convert_to_log_level(level):
if level >= len(log_levels):
level = 3
level = len(log_levels) - 1
return log_levels[level]


Expand Down Expand Up @@ -452,6 +452,12 @@ def report(e):
else:
sys.excepthook(*sys.exc_info())

if isinstance(e, CompilerException):
from inmanta.compiler.help.explainer import ExplainerFactory
helpmsg = ExplainerFactory().explain_and_format(e)
if helpmsg is not None:
print(helpmsg)

try:
options.func(options)
except CLIException as e:
Expand Down
73 changes: 55 additions & 18 deletions src/inmanta/ast/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@
Contact: code@inmanta.com
"""

from inmanta import util

from typing import Dict, Sequence, List, Optional, Union # noqa: F401
from abc import abstractmethod
import traceback
from functools import lru_cache


try:
Expand All @@ -31,11 +30,12 @@

if TYPE_CHECKING:
import inmanta.ast.statements # noqa: F401
from inmanta.ast.attribute import Attribute # noqa: F401
from inmanta.ast.type import Type, NamedType # noqa: F401
from inmanta.execute.runtime import ExecutionContext, Instance # noqa: F401
from inmanta.ast.statements import Statement # noqa: F401
from inmanta.execute.runtime import ExecutionContext, Instance, DelayedResultVariable # noqa: F401
from inmanta.ast.statements import Statement, AssignStatement # noqa: F401
from inmanta.ast.entity import Entity # noqa: F401
from inmanta.ast.statements.define import DefineImport # noqa: F401
from inmanta.ast.statements.define import DefineImport, DefineEntity # noqa: F401


class Location(object):
Expand Down Expand Up @@ -77,7 +77,7 @@ def merge(self, other: Location) -> Location:
assert isinstance(other, Location)
assert self.file == other.file

if isinstance(other, Location):
if not isinstance(other, Range):
return Location(self.file, min(self.lnr, other.lnr))
else:
if other.lnr < self.lnr:
Expand Down Expand Up @@ -135,7 +135,7 @@ class LocatableString(object):
2. in the constructors of other statements
"""

def __init__(self, value, location: Range, lexpos: "int", namespace: "Namespace") -> None:
def __init__(self, value: str, location: Range, lexpos: "int", namespace: "Namespace") -> None:
self.value = value
self.location = location

Expand All @@ -147,13 +147,13 @@ def __init__(self, value, location: Range, lexpos: "int", namespace: "Namespace"
self.lexpos = lexpos
self.namespace = namespace

def get_value(self):
def get_value(self) -> str:
return self.value

def get_location(self):
def get_location(self) -> Location:
return self.location

def __str__(self):
def __str__(self) -> str:
return self.value


Expand Down Expand Up @@ -438,7 +438,7 @@ def _get_ns(self, ns_parts: List[str]) -> "Optional[Namespace]":
return None
return child._get_ns(ns_parts[1:])

@util.memoize
@lru_cache()
def to_path(self) -> List[str]:
"""
Return a list with the namespace path elements in it.
Expand All @@ -456,6 +456,7 @@ def get_location(self) -> Location:


class CompilerException(Exception):
""" Base class for exceptions generated by the compiler"""

def __init__(self, msg: str) -> None:
Exception.__init__(self, msg)
Expand Down Expand Up @@ -483,7 +484,7 @@ def format(self) -> str:
else:
return self.get_message()

def format_trace(self, indent="", indent_level=0):
def format_trace(self, indent: str="", indent_level: int=0) -> str:
"""Make a representation of this exception and its causes"""
out = indent * indent_level + self.format()

Expand All @@ -494,11 +495,12 @@ def format_trace(self, indent="", indent_level=0):

return out

def __str__(self):
def __str__(self) -> str:
return self.format()


class RuntimeException(CompilerException):
"""Baseclass for exceptions raised by the compiler after parsing is complete."""

def __init__(self, stmt: "Optional[Locatable]", msg: str) -> None:
CompilerException.__init__(self, msg)
Expand All @@ -507,7 +509,10 @@ def __init__(self, stmt: "Optional[Locatable]", msg: str) -> None:
self.set_location(stmt.get_location())
self.stmt = stmt

def set_statement(self, stmt: "Locatable", replace: bool = True):
def set_statement(self, stmt: "Locatable", replace: bool = True) -> None:
for cause in self.get_causes():
cause.set_statement(stmt, replace)

if replace or self.stmt is None:
self.set_location(stmt.get_location())
self.stmt = stmt
Expand All @@ -520,6 +525,7 @@ def format(self) -> str:


class TypeNotFoundException(RuntimeException):
"""Exception raised when a type is referenced that does not exist"""

def __init__(self, type: str, ns: Namespace) -> None:
RuntimeException.__init__(self, stmt=None, msg="could not find type %s in namespace %s" % (type, ns))
Expand All @@ -534,6 +540,10 @@ def stringify_exception(exn: Exception) -> str:


class ExternalException(RuntimeException):
"""
When a plugin call produces an exception that is not a RuntimeException,
it is wrapped in an ExternalException to make it conform to the expected interface
"""

def __init__(self, stmt: Locatable, msg: str, cause: Exception) -> None:
RuntimeException.__init__(self, stmt=stmt, msg=msg)
Expand All @@ -543,7 +553,7 @@ def __init__(self, stmt: Locatable, msg: str, cause: Exception) -> None:
def get_causes(self) -> List[CompilerException]:
return []

def format_trace(self, indent="", indent_level=0):
def format_trace(self, indent: str="", indent_level: int=0) -> str:
"""Make a representation of this exception and its causes"""
out = indent * indent_level + self.format()

Expand All @@ -556,6 +566,7 @@ def format_trace(self, indent="", indent_level=0):


class WrappingRuntimeException(RuntimeException):
""" Baseclass for RuntimeExceptions wrapping other RuntimeException """

def __init__(self, stmt: Locatable, msg: str, cause: RuntimeException) -> None:
if stmt is None and isinstance(cause, RuntimeException):
Expand All @@ -570,6 +581,7 @@ def get_causes(self) -> List[CompilerException]:


class AttributeException(WrappingRuntimeException):
""" Exception raise when an attribute could not be set, always wraps another exception """

def __init__(self, stmt: "Locatable", instance: "Instance", attribute: str, cause: RuntimeException) -> None:
WrappingRuntimeException.__init__(
Expand All @@ -579,6 +591,7 @@ def __init__(self, stmt: "Locatable", instance: "Instance", attribute: str, caus


class OptionalValueException(RuntimeException):
"""Exception raised when an optional value is accessed that has no value (and is frozen)"""

def __init__(self, instance: "Instance", attribute: str) -> None:
RuntimeException.__init__(self, instance, "Optional variable accessed that has no value (%s.%s)" %
Expand All @@ -588,10 +601,12 @@ def __init__(self, instance: "Instance", attribute: str) -> None:


class IndexException(RuntimeException):
"""Exception raised when an index definition is invalid"""
pass


class TypingException(RuntimeException):
"""Base class for exceptions raised during the typing phase of compilation"""
pass


Expand All @@ -600,14 +615,16 @@ class KeyException(RuntimeException):


class CycleExcpetion(TypingException):
"""Exception raised when a type is its own parent (type cycle)"""

def __init__(self, first_type, final_name):
def __init__(self, first_type: "DefineEntity", final_name: str) -> None:
super(CycleExcpetion, self).__init__(first_type, None)
self.types = []
self.types = [] # type: List[DefineEntity]
self.complete = False
self.final_name = final_name

def add(self, element):
def add(self, element: "DefineEntity") -> None:
"""Collect parent entities while traveling up the stack"""
if(self.complete):
return
if element.get_full_name() == self.final_name:
Expand Down Expand Up @@ -649,7 +666,26 @@ def __init__(self, stmt: "Statement", value: object, location: Location, newvalu
RuntimeException.__init__(self, stmt, msg)


class ModifiedAfterFreezeException(RuntimeException):

def __init__(self,
rv: "DelayedResultVariable",
instance: "Entity",
attribute: "Attribute",
value: object,
location: Location,
reverse: bool) -> None:
RuntimeException.__init__(self, None, "List modified after freeze")
self.instance = instance
self.attribute = attribute
self.value = value
self.location = location
self.resultvariable = rv
self.reverse = reverse


class DuplicateException(TypingException):
""" Exception raise when something is defined twice """

def __init__(self, stmt: Locatable, other: Locatable, msg: str) -> None:
TypingException.__init__(self, stmt, msg)
Expand All @@ -665,6 +701,7 @@ class CompilerError(Exception):


class MultiException(CompilerException):
"""A single exception collecting multiple CompilerExceptions"""

def __init__(self, others: List[CompilerException]) -> None:
CompilerException.__init__(self, "")
Expand Down
3 changes: 3 additions & 0 deletions src/inmanta/ast/statements/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ def __init__(self,
self._indirect_attributes = {} # type: Dict[str,ExpressionStatement]
self._use_default = set() # type: Set[str]

def pretty_print(self) -> str:
return "%s(%s)" % (self.class_type, ",".join(("%s=%s" % (k, v.pretty_print()) for k, v in self.attributes.items())))

def normalize(self) -> None:
mytype = self.namespace.get_type(self.class_type)

Expand Down
Loading