tecio-python 0.1.0__tar.gz → 0.1.1__tar.gz
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.
- {tecio_python-0.1.0/tecio_python.egg-info → tecio_python-0.1.1}/PKG-INFO +11 -8
- {tecio_python-0.1.0 → tecio_python-0.1.1}/README.md +8 -7
- {tecio_python-0.1.0 → tecio_python-0.1.1}/pyproject.toml +3 -1
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/__init__.py +5 -0
- tecio_python-0.1.1/tecio/_containers.py +290 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/_io.py +190 -214
- tecio_python-0.1.1/tecio/cli/tec2mat.py +454 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/dat/_read.py +350 -117
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/dat/_write.py +45 -23
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/plt/_read.py +134 -98
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/szl/_read.py +94 -24
- {tecio_python-0.1.0 → tecio_python-0.1.1/tecio_python.egg-info}/PKG-INFO +11 -8
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio_python.egg-info/SOURCES.txt +4 -1
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio_python.egg-info/entry_points.txt +1 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio_python.egg-info/requires.txt +3 -0
- tecio_python-0.1.1/tests/test_dat_read.py +438 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tests/test_dat_write.py +0 -0
- tecio_python-0.1.1/tests/test_plt_read.py +265 -0
- tecio_python-0.1.1/tests/test_szl_read.py +262 -0
- tecio_python-0.1.1/tests/test_tec2mat.py +320 -0
- tecio_python-0.1.0/tests/test_dat_read.py +0 -294
- tecio_python-0.1.0/tests/test_plt_read.py +0 -97
- tecio_python-0.1.0/tests/test_szl_read.py +0 -97
- {tecio_python-0.1.0 → tecio_python-0.1.1}/LICENSE +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/NOTICE +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/setup.cfg +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/cli/__init__.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/cli/tecdump.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/cli/tecextract.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/cli/tecfix.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/cli/tecmerge.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/cli/teconvert.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/cli/tecscale.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/cli/tecslice.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/cli/tecstats.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/dat/__init__.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/libtecio.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/plt/__init__.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/plt/_write.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/szl/__init__.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/szl/_write.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio/utils.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio_python.egg-info/dependency_links.txt +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tecio_python.egg-info/top_level.txt +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tests/test_cli.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tests/test_io.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tests/test_libtecio.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tests/test_plt_write.py +0 -0
- {tecio_python-0.1.0 → tecio_python-0.1.1}/tests/test_szl_write.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tecio-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Python interface for reading and writing Tecplot data files
|
|
5
5
|
Project-URL: Homepage, https://github.com/meersman/tecio
|
|
6
6
|
Project-URL: Documentation, https://meersman.github.io/tecio/
|
|
@@ -38,6 +38,8 @@ Requires-Dist: sphinx>=7.0; extra == "docs"
|
|
|
38
38
|
Requires-Dist: sphinx-book-theme>=1.1; extra == "docs"
|
|
39
39
|
Requires-Dist: sphinx-autodoc-typehints>=2.0; extra == "docs"
|
|
40
40
|
Requires-Dist: myst-parser>=3.0; extra == "docs"
|
|
41
|
+
Provides-Extra: mat
|
|
42
|
+
Requires-Dist: scipy; extra == "mat"
|
|
41
43
|
Dynamic: license-file
|
|
42
44
|
|
|
43
45
|
# tecio
|
|
@@ -53,10 +55,10 @@ Python interface for reading and writing Tecplot data files.
|
|
|
53
55
|
|
|
54
56
|
## Overview
|
|
55
57
|
|
|
56
|
-
`tecio` wraps Tecplot's TecIO C library to provide a Python API for
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
`tecio` wraps Tecplot's TecIO C library to provide a Python API for reading
|
|
59
|
+
and writing Tecplot binary formats (`.szplt`, `.plt`) and ASCII (`.dat`). It
|
|
60
|
+
also includes command-line tools for common file operations such as converting
|
|
61
|
+
formats, extracting zones, and computing statistics.
|
|
60
62
|
|
|
61
63
|
**Requirements:** Python 3.10+, NumPy, Tecplot 360 (or the standalone TecIO library)
|
|
62
64
|
|
|
@@ -70,9 +72,8 @@ pip install tecio-python
|
|
|
70
72
|
|
|
71
73
|
The TecIO shared library (`libtecio.so` / `libtecio.dylib`) is not bundled
|
|
72
74
|
with this package and must be provided separately via a Tecplot 360
|
|
73
|
-
installation or the standalone TecIO library. Set the `TECIO_LIB`
|
|
74
|
-
|
|
75
|
-
path:
|
|
75
|
+
installation or the standalone TecIO library. Set the `TECIO_LIB` environment
|
|
76
|
+
variable to point to the library if it is not on your system path:
|
|
76
77
|
|
|
77
78
|
```bash
|
|
78
79
|
export TECIO_LIB=/path/to/libtecio.so # Linux
|
|
@@ -99,6 +100,8 @@ with tecio.open("sine.szplt", "r") as tec:
|
|
|
99
100
|
print(tec.variables) # ['x', 'y']
|
|
100
101
|
x = tec.zone[0].variable[0].values
|
|
101
102
|
y = tec.zone[0].variable[1].values
|
|
103
|
+
# or
|
|
104
|
+
x, y = tec.zone[0].get_array(["x", "y"])
|
|
102
105
|
```
|
|
103
106
|
|
|
104
107
|
## API
|
|
@@ -11,10 +11,10 @@ Python interface for reading and writing Tecplot data files.
|
|
|
11
11
|
|
|
12
12
|
## Overview
|
|
13
13
|
|
|
14
|
-
`tecio` wraps Tecplot's TecIO C library to provide a Python API for
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
`tecio` wraps Tecplot's TecIO C library to provide a Python API for reading
|
|
15
|
+
and writing Tecplot binary formats (`.szplt`, `.plt`) and ASCII (`.dat`). It
|
|
16
|
+
also includes command-line tools for common file operations such as converting
|
|
17
|
+
formats, extracting zones, and computing statistics.
|
|
18
18
|
|
|
19
19
|
**Requirements:** Python 3.10+, NumPy, Tecplot 360 (or the standalone TecIO library)
|
|
20
20
|
|
|
@@ -28,9 +28,8 @@ pip install tecio-python
|
|
|
28
28
|
|
|
29
29
|
The TecIO shared library (`libtecio.so` / `libtecio.dylib`) is not bundled
|
|
30
30
|
with this package and must be provided separately via a Tecplot 360
|
|
31
|
-
installation or the standalone TecIO library. Set the `TECIO_LIB`
|
|
32
|
-
|
|
33
|
-
path:
|
|
31
|
+
installation or the standalone TecIO library. Set the `TECIO_LIB` environment
|
|
32
|
+
variable to point to the library if it is not on your system path:
|
|
34
33
|
|
|
35
34
|
```bash
|
|
36
35
|
export TECIO_LIB=/path/to/libtecio.so # Linux
|
|
@@ -57,6 +56,8 @@ with tecio.open("sine.szplt", "r") as tec:
|
|
|
57
56
|
print(tec.variables) # ['x', 'y']
|
|
58
57
|
x = tec.zone[0].variable[0].values
|
|
59
58
|
y = tec.zone[0].variable[1].values
|
|
59
|
+
# or
|
|
60
|
+
x, y = tec.zone[0].get_array(["x", "y"])
|
|
60
61
|
```
|
|
61
62
|
|
|
62
63
|
## API
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tecio-python"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.1"
|
|
8
8
|
description = "Python interface for reading and writing Tecplot data files"
|
|
9
9
|
|
|
10
10
|
readme = "README.md"
|
|
@@ -45,6 +45,7 @@ tecmerge = "tecio.cli.tecmerge:main"
|
|
|
45
45
|
tecscale = "tecio.cli.tecscale:main"
|
|
46
46
|
tecslice = "tecio.cli.tecslice:main"
|
|
47
47
|
tecstats = "tecio.cli.tecstats:main"
|
|
48
|
+
tec2mat = "tecio.cli.tec2mat:main"
|
|
48
49
|
|
|
49
50
|
[project.optional-dependencies]
|
|
50
51
|
dev = [
|
|
@@ -61,6 +62,7 @@ docs = [
|
|
|
61
62
|
"sphinx-autodoc-typehints>=2.0",
|
|
62
63
|
"myst-parser>=3.0",
|
|
63
64
|
]
|
|
65
|
+
mat = ["scipy"]
|
|
64
66
|
|
|
65
67
|
[tool.setuptools.packages.find]
|
|
66
68
|
include = ["tecio*"]
|
|
@@ -12,12 +12,15 @@ except metadata.PackageNotFoundError:
|
|
|
12
12
|
__version__ = "0.0.0"
|
|
13
13
|
|
|
14
14
|
from . import cli, dat, libtecio, plt, szl, utils
|
|
15
|
+
from ._containers import VariableList, ZoneList
|
|
15
16
|
from ._io import AppendReadWrite, AppendWrite, open
|
|
16
17
|
|
|
17
18
|
# Ensure tecio.open displays as the canonical public name in docs and help().
|
|
18
19
|
open.__module__ = "tecio"
|
|
19
20
|
AppendWrite.__module__ = "tecio"
|
|
20
21
|
AppendReadWrite.__module__ = "tecio"
|
|
22
|
+
ZoneList.__module__ = "tecio"
|
|
23
|
+
VariableList.__module__ = "tecio"
|
|
21
24
|
|
|
22
25
|
__all__ = [
|
|
23
26
|
"libtecio",
|
|
@@ -29,5 +32,7 @@ __all__ = [
|
|
|
29
32
|
"cli",
|
|
30
33
|
"AppendWrite",
|
|
31
34
|
"AppendReadWrite",
|
|
35
|
+
"ZoneList",
|
|
36
|
+
"VariableList",
|
|
32
37
|
"__version__",
|
|
33
38
|
]
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"""Index- and name-based container types for Tecplot data collections.
|
|
2
|
+
|
|
3
|
+
These containers are shared by the ``tecio`` readers: ``Read.zone`` returns a
|
|
4
|
+
:class:`ZoneList` of ``ReadZone`` and ``ReadZone.variable`` returns a
|
|
5
|
+
:class:`VariableList` of ``ReadVariable`` for every supported format (SZL, PLT, DAT).
|
|
6
|
+
They depend only on small structural protocols (``.name`` for variables, ``.variable``
|
|
7
|
+
for zones) so they import nothing from either hierarchy and cannot introduce a circular
|
|
8
|
+
dependency.
|
|
9
|
+
|
|
10
|
+
Access model:
|
|
11
|
+
|
|
12
|
+
reader.zone # ZoneList
|
|
13
|
+
reader.zone[0] # ReadZone (element)
|
|
14
|
+
reader.zone[1:4] # ZoneList (sub-collection, same kind)
|
|
15
|
+
reader.zone[0].variable # VariableList
|
|
16
|
+
reader.zone[0].variable["x"] # ReadVariable (object: .values, .is_passive)
|
|
17
|
+
reader.zone[0].variable[2] # ReadVariable (0-based index)
|
|
18
|
+
|
|
19
|
+
Subscripting always returns an element or a sub-collection *of the same kind* (never a
|
|
20
|
+
raw array). The underlying NumPy data is pulled with ``get_array`` on a single zone,
|
|
21
|
+
which mirrors the pandas ``df[...]`` split: a scalar key returns one array, a list of
|
|
22
|
+
names returns a tuple of arrays (for unpacking)::
|
|
23
|
+
|
|
24
|
+
p = reader.zone[0].get_array("p") # ndarray | None
|
|
25
|
+
p = reader.zone[0].get_array(2) # ndarray | None (0-based index)
|
|
26
|
+
x, y, z = reader.zone[0].get_array(["x", "y", "z"]) # tuple, one per name
|
|
27
|
+
|
|
28
|
+
There is deliberately **no** cross-zone array accessor. To pull one variable across
|
|
29
|
+
many zones (e.g. a transient sequence), iterate explicitly so the outer axis is owned by
|
|
30
|
+
your code, and stack only when you know the result is rectangular::
|
|
31
|
+
|
|
32
|
+
seq = [z.get_array("p") for z in reader.zone] # list[ndarray | None]
|
|
33
|
+
stack = np.stack(seq) # only if shapes all match
|
|
34
|
+
|
|
35
|
+
Name lookup is exact and case-sensitive throughout, so distinct variables such
|
|
36
|
+
as ``"x"`` (a local coordinate) and ``"X"`` (a global coordinate) never
|
|
37
|
+
collide, and names that are not valid Python identifiers (``"X [ft]"``,
|
|
38
|
+
``"p'"``) resolve like any other key.
|
|
39
|
+
|
|
40
|
+
Note:
|
|
41
|
+
``get_array`` returns ``None`` for passive or shared variables, mirroring
|
|
42
|
+
``ReadVariable.values``. A list of length 1 (``get_array(["p"])``) returns a 1-tuple
|
|
43
|
+
``(array,)``, not a bare array, sequence-in always yields tuple-out, matching
|
|
44
|
+
``df[["x"]]`` staying 2-D. A missing variable *name* raises ``KeyError``; an
|
|
45
|
+
out-of-range *index* raises ``IndexError``.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
from __future__ import annotations
|
|
49
|
+
|
|
50
|
+
from collections.abc import Iterator
|
|
51
|
+
from typing import Any, Generic, Protocol, TypeVar, overload
|
|
52
|
+
|
|
53
|
+
import numpy as np
|
|
54
|
+
import numpy.typing as npt
|
|
55
|
+
|
|
56
|
+
# --------------------------------------------------------------------------------------
|
|
57
|
+
# Structural protocols
|
|
58
|
+
# --------------------------------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class _HasName(Protocol):
|
|
62
|
+
"""Minimal protocol satisfied by every format's variable element."""
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def name(self) -> str: ...
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class _HasNameAndValues(Protocol):
|
|
69
|
+
"""A variable element that can surface both its name and its data array."""
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def name(self) -> str: ...
|
|
73
|
+
@property
|
|
74
|
+
def values(self) -> npt.NDArray | None: ...
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class _HasVariableList(Protocol):
|
|
78
|
+
"""A zone element exposing a name/index-addressable variable container."""
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def variable(self) -> VariableList[Any]: ...
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
_VarT = TypeVar("_VarT", bound=_HasName)
|
|
85
|
+
_ZoneT = TypeVar("_ZoneT", bound=_HasVariableList)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# ---------------------------------------------------------------------------
|
|
89
|
+
# Shared dispatch helper
|
|
90
|
+
# ---------------------------------------------------------------------------
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@overload
|
|
94
|
+
def select_variable_arrays(
|
|
95
|
+
variables: VariableList[Any], key: int | str
|
|
96
|
+
) -> npt.NDArray | None: ...
|
|
97
|
+
@overload
|
|
98
|
+
def select_variable_arrays(
|
|
99
|
+
variables: VariableList[Any], key: list[str]
|
|
100
|
+
) -> tuple[npt.NDArray | None, ...]: ...
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def select_variable_arrays(
|
|
104
|
+
variables: VariableList[Any],
|
|
105
|
+
key: int | str | list[str],
|
|
106
|
+
) -> npt.NDArray | None | tuple[npt.NDArray | None, ...]:
|
|
107
|
+
"""Resolve *key* against *variables* and return the underlying array(s).
|
|
108
|
+
|
|
109
|
+
Backs ``ReadZone.get_array`` (and any future ``Zone.get_array``) so the
|
|
110
|
+
scalar-vs-sequence dispatch is defined exactly once.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
variables: The zone's :class:`VariableList`.
|
|
114
|
+
key: A single 0-based index or exact name (→ one array), or a list of exact
|
|
115
|
+
names (→ a tuple of arrays, in the order given).
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
One array (or ``None`` for a passive/shared variable) for a scalar key; a tuple
|
|
119
|
+
of such arrays for a list of names.
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
KeyError: If a name does not exist.
|
|
123
|
+
IndexError: If an index is out of range.
|
|
124
|
+
"""
|
|
125
|
+
if isinstance(key, np.integer):
|
|
126
|
+
key = int(key)
|
|
127
|
+
if isinstance(key, (str, int)):
|
|
128
|
+
return variables[key].values
|
|
129
|
+
return tuple(variables[k].values for k in key)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# ======================================================================================
|
|
133
|
+
# VariableList
|
|
134
|
+
# ======================================================================================
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class VariableList(Generic[_VarT]):
|
|
138
|
+
"""Read-only sequence of variables with positional *and* named access.
|
|
139
|
+
|
|
140
|
+
Drop-in for the ``list`` previously returned by ``ReadZone.variable``:
|
|
141
|
+
iteration, ``len()``, and integer indexing are unchanged. A string key
|
|
142
|
+
resolves a variable by its exact, case-sensitive name.
|
|
143
|
+
|
|
144
|
+
Subscripting returns the variable *object*; use its ``.values`` (or the
|
|
145
|
+
zone's ``get_array``) to obtain the underlying array.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
variables: Ordered list of variable elements (each exposing ``.name``).
|
|
149
|
+
|
|
150
|
+
Note:
|
|
151
|
+
If two variables share a name (rare), the first occurrence wins for
|
|
152
|
+
name-based lookup.
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
__slots__ = ("_items", "_name_index")
|
|
156
|
+
|
|
157
|
+
def __init__(self, variables: list[_VarT]) -> None:
|
|
158
|
+
self._items: list[_VarT] = variables
|
|
159
|
+
# Built lazily on first name lookup so purely positional use pays no
|
|
160
|
+
# cost. For SZL this also avoids issuing a C call per variable name
|
|
161
|
+
# until a name is actually requested.
|
|
162
|
+
self._name_index: dict[str, int] | None = None
|
|
163
|
+
|
|
164
|
+
def _index(self) -> dict[str, int]:
|
|
165
|
+
"""Return (building once) the ``name -> position`` lookup table."""
|
|
166
|
+
if self._name_index is None:
|
|
167
|
+
index: dict[str, int] = {}
|
|
168
|
+
for i, var in enumerate(self._items):
|
|
169
|
+
index.setdefault(var.name, i) # first occurrence wins
|
|
170
|
+
self._name_index = index
|
|
171
|
+
return self._name_index
|
|
172
|
+
|
|
173
|
+
def __len__(self) -> int:
|
|
174
|
+
return len(self._items)
|
|
175
|
+
|
|
176
|
+
def __iter__(self) -> Iterator[_VarT]:
|
|
177
|
+
return iter(self._items)
|
|
178
|
+
|
|
179
|
+
def __getitem__(self, key: int | str) -> _VarT:
|
|
180
|
+
"""Return a variable object by 0-based index or exact name.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
key: A 0-based integer index or an exact, case-sensitive variable
|
|
184
|
+
name.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
The matching variable element.
|
|
188
|
+
|
|
189
|
+
Raises:
|
|
190
|
+
KeyError: If *key* is a name that does not exist.
|
|
191
|
+
IndexError: If *key* is an out-of-range index.
|
|
192
|
+
"""
|
|
193
|
+
if isinstance(key, str):
|
|
194
|
+
try:
|
|
195
|
+
return self._items[self._index()[key]]
|
|
196
|
+
except KeyError:
|
|
197
|
+
raise KeyError(
|
|
198
|
+
f"No variable named {key!r}. Available: {self.names()}"
|
|
199
|
+
) from None
|
|
200
|
+
return self._items[key]
|
|
201
|
+
|
|
202
|
+
def __contains__(self, key: object) -> bool:
|
|
203
|
+
if isinstance(key, str):
|
|
204
|
+
return key in self._index()
|
|
205
|
+
return key in self._items
|
|
206
|
+
|
|
207
|
+
def names(self) -> list[str]:
|
|
208
|
+
"""Return the variable names in dataset order."""
|
|
209
|
+
return [var.name for var in self._items]
|
|
210
|
+
|
|
211
|
+
def __repr__(self) -> str:
|
|
212
|
+
n = len(self._items)
|
|
213
|
+
if n == 0:
|
|
214
|
+
return "VariableList([])"
|
|
215
|
+
head = 20
|
|
216
|
+
lines = [f" {v!r}," for v in self._items[:head]]
|
|
217
|
+
if n > head:
|
|
218
|
+
more = n - head
|
|
219
|
+
lines.append(f" ... and {more} more variable{'' if more == 1 else 's'},")
|
|
220
|
+
body = "\n".join(lines)
|
|
221
|
+
return f"VariableList([\n{body}\n])"
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
# ======================================================================================
|
|
225
|
+
# ZoneList
|
|
226
|
+
# ======================================================================================
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class ZoneList(Generic[_ZoneT]):
|
|
230
|
+
"""Read-only sequence of zones: positional access and slicing only.
|
|
231
|
+
|
|
232
|
+
Drop-in for the ``list`` previously returned by ``Read.zone``: iteration, ``len()``,
|
|
233
|
+
and integer indexing are unchanged. Slicing returns another :class:`ZoneList` (not a
|
|
234
|
+
plain ``list``) so navigation composes.
|
|
235
|
+
|
|
236
|
+
This container deliberately exposes **no** data-extraction method. Pulling one
|
|
237
|
+
variable across many zones is an explicit loop over the zones, keeping the outer
|
|
238
|
+
(zone) axis owned by the caller (see the module docs).
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
zones: Ordered list of zone elements (each exposing ``.variable``).
|
|
242
|
+
"""
|
|
243
|
+
|
|
244
|
+
__slots__ = ("_items",)
|
|
245
|
+
|
|
246
|
+
def __init__(self, zones: list[_ZoneT]) -> None:
|
|
247
|
+
self._items: list[_ZoneT] = zones
|
|
248
|
+
|
|
249
|
+
def __len__(self) -> int:
|
|
250
|
+
return len(self._items)
|
|
251
|
+
|
|
252
|
+
def __iter__(self) -> Iterator[_ZoneT]:
|
|
253
|
+
return iter(self._items)
|
|
254
|
+
|
|
255
|
+
def __contains__(self, item: object) -> bool:
|
|
256
|
+
return item in self._items
|
|
257
|
+
|
|
258
|
+
@overload
|
|
259
|
+
def __getitem__(self, key: int) -> _ZoneT: ...
|
|
260
|
+
@overload
|
|
261
|
+
def __getitem__(self, key: slice) -> ZoneList[_ZoneT]: ...
|
|
262
|
+
|
|
263
|
+
def __getitem__(self, key: int | slice) -> _ZoneT | ZoneList[_ZoneT]:
|
|
264
|
+
"""Index a single zone or slice a sub-collection of zones.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
key: A 0-based zone index, or a ``slice``.
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
A zone element (``int`` key) or a new :class:`ZoneList` (``slice``
|
|
271
|
+
key).
|
|
272
|
+
|
|
273
|
+
Raises:
|
|
274
|
+
IndexError: If an integer *key* is out of range.
|
|
275
|
+
"""
|
|
276
|
+
if isinstance(key, slice):
|
|
277
|
+
return ZoneList(self._items[key])
|
|
278
|
+
return self._items[key]
|
|
279
|
+
|
|
280
|
+
def __repr__(self) -> str:
|
|
281
|
+
n = len(self._items)
|
|
282
|
+
if n == 0:
|
|
283
|
+
return "ZoneList([])"
|
|
284
|
+
head = 5
|
|
285
|
+
lines = [f" {z!r}," for z in self._items[:head]]
|
|
286
|
+
if n > head:
|
|
287
|
+
more = n - head
|
|
288
|
+
lines.append(f" ... and {more} more zone{'' if more == 1 else 's'},")
|
|
289
|
+
body = "\n".join(lines)
|
|
290
|
+
return f"ZoneList([\n{body}\n])"
|