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.
@@ -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)
Binary file
@@ -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
+ ]