gecode-iris 0.1.0__cp310-abi3-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gecode_iris/__init__.py +15 -0
- gecode_iris/_cli.py +20 -0
- gecode_iris/_data.py +24 -0
- gecode_iris/_error.py +89 -0
- gecode_iris/_native.pyd +0 -0
- gecode_iris/_native.pyi +96 -0
- gecode_iris/build/__init__.py +204 -0
- gecode_iris/build/__init__.pyi +1434 -0
- gecode_iris/build/_check.py +321 -0
- gecode_iris/build/_error.py +93 -0
- gecode_iris/build/_expr.py +625 -0
- gecode_iris/build/_ops.py +171 -0
- gecode_iris/build/_pattern.py +207 -0
- gecode_iris/build/_print.py +835 -0
- gecode_iris/build/_scope.py +796 -0
- gecode_iris/build/_site.py +66 -0
- gecode_iris/build/_state.py +539 -0
- gecode_iris/build/_types.py +276 -0
- gecode_iris/build/gecode.py +11 -0
- gecode_iris/build/gecode.pyi +8 -0
- gecode_iris/py.typed +0 -0
- gecode_iris-0.1.0.dist-info/METADATA +470 -0
- gecode_iris-0.1.0.dist-info/RECORD +27 -0
- gecode_iris-0.1.0.dist-info/WHEEL +4 -0
- gecode_iris-0.1.0.dist-info/entry_points.txt +2 -0
- gecode_iris-0.1.0.dist-info/licenses/LICENSE +21 -0
- gecode_iris-0.1.0.dist-info/sboms/gecode-iris-python.cyclonedx.json +2718 -0
gecode_iris/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Read text instance files with Iris reader specs.
|
|
2
|
+
|
|
3
|
+
Parse a reader spec with ``Spec.parse`` or ``Spec.load``, then read an
|
|
4
|
+
input with ``spec.read``. Language errors raise ``Error``. Build a reader
|
|
5
|
+
spec in Python with typed handles in ``gecode_iris.build``.
|
|
6
|
+
|
|
7
|
+
The package also installs the ``iris`` command, the same command line as the
|
|
8
|
+
Rust ``iris`` binary.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from gecode_iris._data import Entry, Value
|
|
12
|
+
from gecode_iris._error import CallSite, Error
|
|
13
|
+
from gecode_iris._native import Data, Spec
|
|
14
|
+
|
|
15
|
+
__all__ = ["CallSite", "Data", "Entry", "Error", "Spec", "Value"]
|
gecode_iris/_cli.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""The ``iris`` console script, which runs the Rust ``iris`` command line."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import signal
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from gecode_iris._native import run_cli
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main() -> None:
|
|
12
|
+
"""Run ``iris`` on this process's arguments and exit with its status."""
|
|
13
|
+
# The Rust code writes to the process's file descriptors directly.
|
|
14
|
+
sys.stdout.flush()
|
|
15
|
+
sys.stderr.flush()
|
|
16
|
+
# Python's handler would only take effect once the Rust code returns, so
|
|
17
|
+
# restore the default; an inherited SIG_IGN or a custom handler stays.
|
|
18
|
+
if signal.getsignal(signal.SIGINT) is signal.default_int_handler:
|
|
19
|
+
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
|
20
|
+
sys.exit(run_cli(sys.argv))
|
gecode_iris/_data.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""The Python types of what ``Data`` holds."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Literal, NamedTuple, TypeAlias
|
|
6
|
+
|
|
7
|
+
Value: TypeAlias = int | float | str | list["Value"]
|
|
8
|
+
"""An entry value: ``int``, ``float`` or ``str`` at depth 0, and a list of
|
|
9
|
+
values one level shallower at each greater depth."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Entry(NamedTuple):
|
|
13
|
+
"""The name, value type and depth of one entry in ``Data``."""
|
|
14
|
+
|
|
15
|
+
name: str
|
|
16
|
+
"""The entry name."""
|
|
17
|
+
value_type: Literal["int", "float", "string"]
|
|
18
|
+
"""The type of the entry's scalars."""
|
|
19
|
+
depth: int
|
|
20
|
+
"""The number of array levels: 0 for a scalar, 1 for a row, and so on."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# The package exports this class, so pickles name it there.
|
|
24
|
+
Entry.__module__ = "gecode_iris"
|
gecode_iris/_error.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""The exception for Iris language errors."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import NamedTuple
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CallSite(NamedTuple):
|
|
9
|
+
"""A place in the host program: where a builder statement was called.
|
|
10
|
+
|
|
11
|
+
``str(site)`` is ``file:line``.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
filename: str
|
|
15
|
+
"""The file of the calling code, as its code object names it."""
|
|
16
|
+
line: int
|
|
17
|
+
"""The line of the call, counting from 1."""
|
|
18
|
+
function: str
|
|
19
|
+
"""The name of the calling function, such as ``<module>``."""
|
|
20
|
+
|
|
21
|
+
def __str__(self) -> str:
|
|
22
|
+
return f"{self.filename}:{self.line}"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# The package exports this class, so pickles name it there.
|
|
26
|
+
CallSite.__module__ = "gecode_iris"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Error(Exception):
|
|
30
|
+
"""A language error from parsing a reader spec or reading an input.
|
|
31
|
+
|
|
32
|
+
``str(error)`` is the full diagnostic. The location is the input
|
|
33
|
+
location when there is one, and otherwise the reader spec location;
|
|
34
|
+
``line``, ``column`` and ``origin`` are each ``None`` when unknown.
|
|
35
|
+
``path`` is the record path of a failed check over records.
|
|
36
|
+
|
|
37
|
+
Host failures, such as a missing file, raise built-in exceptions
|
|
38
|
+
instead and have no error code.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
# The package exports this class, so pickles and tracebacks name it there.
|
|
42
|
+
__module__ = "gecode_iris"
|
|
43
|
+
|
|
44
|
+
code: str
|
|
45
|
+
"""The stable error code, such as ``E-CHECK-FAILED``."""
|
|
46
|
+
message: str
|
|
47
|
+
"""The message, without location or excerpt."""
|
|
48
|
+
line: int | None
|
|
49
|
+
"""The line of the location, counting from 1."""
|
|
50
|
+
column: int | None
|
|
51
|
+
"""The byte column of the location, counting from 1."""
|
|
52
|
+
path: tuple[int, ...] | None
|
|
53
|
+
"""The record path of a failed check over records."""
|
|
54
|
+
origin: str | None
|
|
55
|
+
"""The input or reader spec origin of the location, such as its path."""
|
|
56
|
+
call_site: CallSite | None
|
|
57
|
+
"""Where the statement the error belongs to was called, for a spec built
|
|
58
|
+
with ``gecode_iris.build``; ``None`` for a spec parsed from text."""
|
|
59
|
+
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
text: str,
|
|
63
|
+
code: str,
|
|
64
|
+
message: str,
|
|
65
|
+
line: int | None,
|
|
66
|
+
column: int | None,
|
|
67
|
+
path: tuple[int, ...] | None,
|
|
68
|
+
origin: str | None,
|
|
69
|
+
document: str,
|
|
70
|
+
call_site: CallSite | None = None,
|
|
71
|
+
) -> None:
|
|
72
|
+
super().__init__(text)
|
|
73
|
+
self.code = code
|
|
74
|
+
self.message = message
|
|
75
|
+
self.line = line
|
|
76
|
+
self.column = column
|
|
77
|
+
self.path = path
|
|
78
|
+
self.origin = origin
|
|
79
|
+
self.call_site = call_site
|
|
80
|
+
self._document = document
|
|
81
|
+
|
|
82
|
+
def to_json(self) -> str:
|
|
83
|
+
"""Return the canonical JSON error document."""
|
|
84
|
+
return self._document
|
|
85
|
+
|
|
86
|
+
def __reduce__(self) -> tuple[type[Error], tuple[object, ...]]:
|
|
87
|
+
text = self.args[0]
|
|
88
|
+
fields = (self.code, self.message, self.line, self.column, self.path, self.origin)
|
|
89
|
+
return type(self), (text, *fields, self._document, self.call_site)
|
gecode_iris/_native.pyd
ADDED
|
Binary file
|
gecode_iris/_native.pyi
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from collections.abc import Mapping, Sequence
|
|
2
|
+
from os import PathLike
|
|
3
|
+
from typing import Any, TypeVar, final, overload
|
|
4
|
+
|
|
5
|
+
from numpy.typing import NDArray
|
|
6
|
+
|
|
7
|
+
from gecode_iris._data import Entry, Value
|
|
8
|
+
from gecode_iris.build import Field
|
|
9
|
+
|
|
10
|
+
_T = TypeVar("_T")
|
|
11
|
+
|
|
12
|
+
@final
|
|
13
|
+
class Spec:
|
|
14
|
+
"""A parsed and checked reader spec.
|
|
15
|
+
|
|
16
|
+
One ``Spec`` can read any number of inputs, and threads can share it.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def parse(text: str | bytes | bytearray, origin: str = "<string>") -> Spec:
|
|
21
|
+
"""Parse and check a reader spec given as ``str`` or ``bytes``."""
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def _parse_built(text: str, origin: str, call_sites: Any) -> Spec: ...
|
|
25
|
+
@property
|
|
26
|
+
def _call_sites(self) -> Any: ...
|
|
27
|
+
@staticmethod
|
|
28
|
+
def load(path: str | PathLike[str]) -> Spec:
|
|
29
|
+
"""Read and parse a reader spec file; the path is the error origin."""
|
|
30
|
+
|
|
31
|
+
def read(
|
|
32
|
+
self,
|
|
33
|
+
source: bytes | bytearray | str | PathLike[str],
|
|
34
|
+
params: Mapping[str, str] | None = None,
|
|
35
|
+
*,
|
|
36
|
+
origin: str | None = None,
|
|
37
|
+
) -> Data:
|
|
38
|
+
"""Read one instance from input bytes or from a path."""
|
|
39
|
+
|
|
40
|
+
def catalog(
|
|
41
|
+
self,
|
|
42
|
+
source: bytes | bytearray | str | PathLike[str],
|
|
43
|
+
params: Mapping[str, str] | None = None,
|
|
44
|
+
*,
|
|
45
|
+
origin: str | None = None,
|
|
46
|
+
) -> Data:
|
|
47
|
+
"""List the instances in the input, with the same arguments as ``read``."""
|
|
48
|
+
|
|
49
|
+
@final
|
|
50
|
+
class Data:
|
|
51
|
+
"""The data from one read or catalog.
|
|
52
|
+
|
|
53
|
+
``data[name]`` returns an entry's value and ``name in data`` tests for an
|
|
54
|
+
entry. Values are copied into Python objects on each access: an ``int``,
|
|
55
|
+
``float`` or ``str`` at depth 0, and nested lists at greater depths.
|
|
56
|
+
|
|
57
|
+
A handle from ``gecode_iris.build`` can stand for the name, and then
|
|
58
|
+
``data[handle]`` has the handle's type: ``None`` when an optional entry is
|
|
59
|
+
absent. A handle whose entry has another value type or depth, because a
|
|
60
|
+
later statement redefined its name or the handle belongs to another reader
|
|
61
|
+
spec, raises ``TypeError``.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def entries(self) -> tuple[Entry, ...]:
|
|
66
|
+
"""The entries in the order their names were defined."""
|
|
67
|
+
|
|
68
|
+
@overload
|
|
69
|
+
def __getitem__(self, key: str, /) -> Value: ...
|
|
70
|
+
@overload
|
|
71
|
+
def __getitem__(self, key: Field[_T], /) -> _T: ...
|
|
72
|
+
def __getitem__(self, key: str | Field[Any], /) -> Any:
|
|
73
|
+
"""The whole value of an entry; raises ``KeyError`` for an unknown name."""
|
|
74
|
+
|
|
75
|
+
def __contains__(self, key: object, /) -> bool: ...
|
|
76
|
+
def ints(self, key: str | Field[Any], /, *path: int) -> list[int]:
|
|
77
|
+
"""The integer row of an entry at a path."""
|
|
78
|
+
|
|
79
|
+
def array(self, key: str | Field[Any], /, *path: int) -> NDArray[Any] | list[Value]:
|
|
80
|
+
"""An array of an entry, or of its row at a path.
|
|
81
|
+
|
|
82
|
+
The result is a ``numpy.ndarray`` when numpy is importable and the
|
|
83
|
+
array is rectangular, and nested lists otherwise.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def to_dict(self) -> dict[str, Value]:
|
|
87
|
+
"""All entries as a ``dict`` from name to value, in entry order."""
|
|
88
|
+
|
|
89
|
+
def to_json(self) -> str:
|
|
90
|
+
"""The canonical JSON document, ending in a newline."""
|
|
91
|
+
|
|
92
|
+
def to_dzn(self) -> str:
|
|
93
|
+
"""The MiniZinc data text defined in the Specification's appendix B."""
|
|
94
|
+
|
|
95
|
+
def run_cli(argv: Sequence[str]) -> int:
|
|
96
|
+
"""Run the ``iris`` command line on ``argv``, program name first; return its exit status."""
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""Build reader specs from Python code, with typed handles.
|
|
2
|
+
|
|
3
|
+
A :class:`SpecBuilder` is the root scope of a spec. Each statement is a method
|
|
4
|
+
call, a block is a ``with`` statement, and every definition returns a handle
|
|
5
|
+
that later statements use in expressions and that ``data[handle]`` reads from
|
|
6
|
+
the data with the type it promises::
|
|
7
|
+
|
|
8
|
+
import gecode_iris.build as ib
|
|
9
|
+
|
|
10
|
+
b = ib.SpecBuilder("jobshop")
|
|
11
|
+
jobs = b.read("jobs", ib.int_range(1))
|
|
12
|
+
machines = b.read("machines", ib.int_range(1))
|
|
13
|
+
with b.repeat(jobs) as job:
|
|
14
|
+
with job.repeat(machines) as task:
|
|
15
|
+
machine = task.read("machine", ib.int_range(0, machines - 1))
|
|
16
|
+
duration = task.read("duration", ib.at_least(0))
|
|
17
|
+
spec = b.build()
|
|
18
|
+
|
|
19
|
+
data = spec.read(b"1 2\\n0 3 1 4\\n")
|
|
20
|
+
data[duration] # [[3, 4]], typed list[list[int]]
|
|
21
|
+
|
|
22
|
+
Python operators build Iris expressions, except for division, remainder and
|
|
23
|
+
power, which are :func:`div`, :func:`rem` and :func:`pow`, and the logical
|
|
24
|
+
operators, which are ``&``, ``|`` and ``~``. Line patterns are t-strings on
|
|
25
|
+
Python 3.14 and later, and tuples of text and parts on every version.
|
|
26
|
+
|
|
27
|
+
A mistake that a builder can find raises :class:`BuildError` at the call that
|
|
28
|
+
made it, or from ``build()``, and names the caller's file and line. Errors
|
|
29
|
+
while reading a built spec name the call site of their statement in
|
|
30
|
+
:attr:`gecode_iris.Error.call_site`.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from typing import NoReturn
|
|
34
|
+
|
|
35
|
+
from gecode_iris._error import CallSite
|
|
36
|
+
from gecode_iris.build import gecode
|
|
37
|
+
from gecode_iris.build._error import BuildError, BuildErrorKind
|
|
38
|
+
from gecode_iris.build._expr import (
|
|
39
|
+
DynExpr,
|
|
40
|
+
DynField,
|
|
41
|
+
Expr,
|
|
42
|
+
Field,
|
|
43
|
+
FloatExpr,
|
|
44
|
+
FloatField,
|
|
45
|
+
FloatLookup,
|
|
46
|
+
IntExpr,
|
|
47
|
+
IntField,
|
|
48
|
+
IntLookup,
|
|
49
|
+
NumExpr,
|
|
50
|
+
NumField,
|
|
51
|
+
NumLookup,
|
|
52
|
+
Record,
|
|
53
|
+
RecordExpr,
|
|
54
|
+
RecordList,
|
|
55
|
+
StrExpr,
|
|
56
|
+
StrField,
|
|
57
|
+
StrLookup,
|
|
58
|
+
all,
|
|
59
|
+
any,
|
|
60
|
+
count,
|
|
61
|
+
defined,
|
|
62
|
+
distinct,
|
|
63
|
+
div,
|
|
64
|
+
lit,
|
|
65
|
+
max,
|
|
66
|
+
min,
|
|
67
|
+
over,
|
|
68
|
+
pow,
|
|
69
|
+
rem,
|
|
70
|
+
size,
|
|
71
|
+
sum,
|
|
72
|
+
)
|
|
73
|
+
from gecode_iris.build._pattern import (
|
|
74
|
+
Discard,
|
|
75
|
+
Part,
|
|
76
|
+
bind,
|
|
77
|
+
blank,
|
|
78
|
+
discard,
|
|
79
|
+
discard_as,
|
|
80
|
+
ref,
|
|
81
|
+
rest_of_line,
|
|
82
|
+
validate,
|
|
83
|
+
)
|
|
84
|
+
from gecode_iris.build._scope import (
|
|
85
|
+
ArmGuard,
|
|
86
|
+
Arms,
|
|
87
|
+
Block,
|
|
88
|
+
Scope,
|
|
89
|
+
Scope0,
|
|
90
|
+
Scope1,
|
|
91
|
+
Scope2,
|
|
92
|
+
Scope3,
|
|
93
|
+
ScopeN,
|
|
94
|
+
SectionsGuard,
|
|
95
|
+
SpecBuilder,
|
|
96
|
+
declared,
|
|
97
|
+
)
|
|
98
|
+
from gecode_iris.build._site import track_caller
|
|
99
|
+
from gecode_iris.build._types import (
|
|
100
|
+
Alphabet,
|
|
101
|
+
Eol,
|
|
102
|
+
FloatType,
|
|
103
|
+
IntType,
|
|
104
|
+
ListReading,
|
|
105
|
+
NumType,
|
|
106
|
+
StrType,
|
|
107
|
+
Type,
|
|
108
|
+
alphabet,
|
|
109
|
+
at_least,
|
|
110
|
+
eol,
|
|
111
|
+
float_,
|
|
112
|
+
int_,
|
|
113
|
+
int_range,
|
|
114
|
+
num,
|
|
115
|
+
rest,
|
|
116
|
+
word,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
Required = NoReturn
|
|
120
|
+
"""The presence of a scope whose statements always execute."""
|
|
121
|
+
|
|
122
|
+
Maybe = None
|
|
123
|
+
"""The presence of a scope whose statements may not execute."""
|
|
124
|
+
|
|
125
|
+
__all__ = [
|
|
126
|
+
"Alphabet",
|
|
127
|
+
"ArmGuard",
|
|
128
|
+
"Arms",
|
|
129
|
+
"Block",
|
|
130
|
+
"BuildError",
|
|
131
|
+
"BuildErrorKind",
|
|
132
|
+
"CallSite",
|
|
133
|
+
"Discard",
|
|
134
|
+
"DynExpr",
|
|
135
|
+
"DynField",
|
|
136
|
+
"Eol",
|
|
137
|
+
"Expr",
|
|
138
|
+
"Field",
|
|
139
|
+
"FloatExpr",
|
|
140
|
+
"FloatField",
|
|
141
|
+
"FloatLookup",
|
|
142
|
+
"FloatType",
|
|
143
|
+
"IntExpr",
|
|
144
|
+
"IntField",
|
|
145
|
+
"IntLookup",
|
|
146
|
+
"IntType",
|
|
147
|
+
"ListReading",
|
|
148
|
+
"Maybe",
|
|
149
|
+
"NumExpr",
|
|
150
|
+
"NumField",
|
|
151
|
+
"NumLookup",
|
|
152
|
+
"NumType",
|
|
153
|
+
"Part",
|
|
154
|
+
"Record",
|
|
155
|
+
"RecordExpr",
|
|
156
|
+
"RecordList",
|
|
157
|
+
"Required",
|
|
158
|
+
"Scope",
|
|
159
|
+
"Scope0",
|
|
160
|
+
"Scope1",
|
|
161
|
+
"Scope2",
|
|
162
|
+
"Scope3",
|
|
163
|
+
"ScopeN",
|
|
164
|
+
"SectionsGuard",
|
|
165
|
+
"SpecBuilder",
|
|
166
|
+
"StrExpr",
|
|
167
|
+
"StrField",
|
|
168
|
+
"StrLookup",
|
|
169
|
+
"StrType",
|
|
170
|
+
"Type",
|
|
171
|
+
"all",
|
|
172
|
+
"alphabet",
|
|
173
|
+
"any",
|
|
174
|
+
"at_least",
|
|
175
|
+
"bind",
|
|
176
|
+
"blank",
|
|
177
|
+
"count",
|
|
178
|
+
"declared",
|
|
179
|
+
"defined",
|
|
180
|
+
"discard",
|
|
181
|
+
"discard_as",
|
|
182
|
+
"distinct",
|
|
183
|
+
"div",
|
|
184
|
+
"eol",
|
|
185
|
+
"float_",
|
|
186
|
+
"gecode",
|
|
187
|
+
"int_",
|
|
188
|
+
"int_range",
|
|
189
|
+
"lit",
|
|
190
|
+
"max",
|
|
191
|
+
"min",
|
|
192
|
+
"num",
|
|
193
|
+
"over",
|
|
194
|
+
"pow",
|
|
195
|
+
"ref",
|
|
196
|
+
"rem",
|
|
197
|
+
"rest",
|
|
198
|
+
"rest_of_line",
|
|
199
|
+
"size",
|
|
200
|
+
"sum",
|
|
201
|
+
"track_caller",
|
|
202
|
+
"validate",
|
|
203
|
+
"word",
|
|
204
|
+
]
|