Source code for minizinc.error

#  This Source Code Form is subject to the terms of the Mozilla Public
#  License, v. 2.0. If a copy of the MPL was not distributed with this
#  file, You can obtain one at

import re
from dataclasses import dataclass
from pathlib import Path
from typing import Optional, Tuple

[docs]@dataclass class Location: """Representation of a location within a file Attributes: file (Optional[Path]): Path to the file line (int): Line within the file (default: ``0``) columns (Tuple[int,int]): Columns on the line, from/to (default: ``(0, 0)``) """ file: Optional[Path] lines: Tuple[int, int] = (0, 0) columns: Tuple[int, int] = (0, 0)
[docs]class ConfigurationError(Exception): """Exception raised during the configuration of MiniZinc Attributes: message (str): Explanation of the error """ message: str
[docs]class MiniZincError(Exception): """Exception raised for errors caused by a MiniZinc Driver Attributes: location (Optional[Location]): File location of the error message (str): Explanation of the error """ location: Optional[Location] message: str def __init__(self, location: Optional[Location] = None, message: str = ""): super().__init__(message) self.location = location
class MiniZincWarning(Warning): """Warning created for warnings originating from a MiniZinc Driver"""
[docs]class EvaluationError(MiniZincError): """Exception raised for errors due to an error during instance evaluation by the MiniZinc Driver""" pass
class AssertionError(EvaluationError): """Exception raised for MiniZinc assertions that failed during instance evaluation""" pass class TypeError(MiniZincError): """Exception raised for type errors found in an MiniZinc Instance""" pass class IncludeError(MiniZincError): """Exception raised for type errors found in an MiniZinc Instance""" pass class CyclicIncludeError(MiniZincError): """Exception raised for type errors found in an MiniZinc Instance""" pass class SyntaxError(MiniZincError): """Exception raised for syntax errors found in an MiniZinc Instance""" pass
[docs]def parse_error(error_txt: bytes) -> MiniZincError: """Parse error from bytes array (raw string) Parse error scans the output from a MiniZinc driver to generate the appropriate MiniZincError. It will make the distinction between different kinds of errors as found by MiniZinc and tries to parse the relevant information to the error. The different kinds of errors are represented by different sub-classes of MiniZincError. Args: error_txt (bytes): raw string containing a MiniZinc error. Generally this should be the error stream of a driver. Returns: An error generated from the string """ error = MiniZincError if b"MiniZinc: evaluation error:" in error_txt: error = EvaluationError if b"Assertion failed:" in error_txt: error = AssertionError elif b"MiniZinc: type error:" in error_txt: error = TypeError elif b"Error: syntax error" in error_txt: error = SyntaxError location = None match ="([^\s]+):(\d+)(.(\d+)-(\d+))?:\s", error_txt) if match: columns = (0, 0) if match[3]: columns = (int(match[4].decode()), int(match[5].decode())) lines = (int(match[2].decode()), int(match[2].decode())) location = Location(Path(match[1].decode()), lines, columns) message = error_txt.decode().strip() if not message: message = ( "MiniZinc stopped with a non-zero exit code, but did not output an " "error message. " ) elif location is not None and location.file is not None and location.file.exists(): with as f: for _ in range(location.lines[0] - 2): f.readline() message += "\nFile fragment:\n" for nr in range(max(1, location.lines[0] - 1), location.lines[1] + 2): line = f.readline() if line == "": break message += f"{nr}: {line.rstrip()}\n" diff = location.columns[1] - location.columns[0] if ( (location.lines[0] == location.lines[1]) and nr == location.lines[0] and diff > 0 ): message += ( " " * (len(str(nr)) + 2 + location.columns[0] - 1) + "^" * (diff + 1) + "\n" ) return error(location, message)
def error_from_stream_obj(obj): """Convert object from JSON stream into MiniZinc Python error Args: obj (Dict): Parsed JSON object from the ``--json-stream`` mode of MiniZinc. Returns: An error generated from the object """ assert obj["type"] == "error" error = MiniZincError if obj["what"] == "syntax error": error = SyntaxError elif obj["what"] == "type error": error = TypeError elif obj["what"] == "include error": error = IncludeError elif obj["what"] == "cyclic include error": error = CyclicIncludeError elif obj["what"] == "evaluation error": error = EvaluationError elif obj["what"] == "assertion failed": error = AssertionError location = None if "location" in obj: location = Location( obj["location"]["filename"], (obj["location"]["firstLine"], obj["location"]["lastLine"]), (obj["location"]["firstColumn"], obj["location"]["lastColumn"]), ) # TODO: Process stack information message = ( "MiniZinc stopped with a non-zero exit code, but did not output an " "error message. " ) if "message" in obj: message = obj["message"] elif "cycle" in obj: message = " includes ".join(obj["cycle"]) return error(location, message)