tecio-python 0.2.3__tar.gz → 0.2.4__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.
Files changed (44) hide show
  1. {tecio_python-0.2.3/tecio_python.egg-info → tecio_python-0.2.4}/PKG-INFO +1 -1
  2. {tecio_python-0.2.3 → tecio_python-0.2.4}/pyproject.toml +1 -1
  3. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/__init__.py +1 -1
  4. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/dat/_read.py +260 -30
  5. {tecio_python-0.2.3 → tecio_python-0.2.4/tecio_python.egg-info}/PKG-INFO +1 -1
  6. {tecio_python-0.2.3 → tecio_python-0.2.4}/LICENSE +0 -0
  7. {tecio_python-0.2.3 → tecio_python-0.2.4}/NOTICE +0 -0
  8. {tecio_python-0.2.3 → tecio_python-0.2.4}/README.md +0 -0
  9. {tecio_python-0.2.3 → tecio_python-0.2.4}/setup.cfg +0 -0
  10. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/_containers.py +0 -0
  11. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/_io.py +0 -0
  12. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/_meta.py +0 -0
  13. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/_utils.py +0 -0
  14. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/__init__.py +0 -0
  15. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tec2mat.py +0 -0
  16. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tecaux.py +0 -0
  17. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tecdump.py +0 -0
  18. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tecextract.py +0 -0
  19. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tecfix.py +0 -0
  20. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tecmerge.py +0 -0
  21. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/teconvert.py +0 -0
  22. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tecscale.py +0 -0
  23. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tecslice.py +0 -0
  24. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/cli/tecstats.py +0 -0
  25. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/dat/__init__.py +0 -0
  26. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/dat/_write.py +0 -0
  27. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/libtecio.py +0 -0
  28. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/plt/__init__.py +0 -0
  29. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/plt/_read.py +0 -0
  30. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/plt/_write.py +0 -0
  31. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/plugins/paraview/TecplotTecioReader.py +0 -0
  32. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/szl/__init__.py +0 -0
  33. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/szl/_read.py +0 -0
  34. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio/szl/_write.py +0 -0
  35. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio_python.egg-info/SOURCES.txt +0 -0
  36. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio_python.egg-info/dependency_links.txt +0 -0
  37. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio_python.egg-info/entry_points.txt +0 -0
  38. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio_python.egg-info/requires.txt +0 -0
  39. {tecio_python-0.2.3 → tecio_python-0.2.4}/tecio_python.egg-info/top_level.txt +0 -0
  40. {tecio_python-0.2.3 → tecio_python-0.2.4}/tests/test_cli.py +0 -0
  41. {tecio_python-0.2.3 → tecio_python-0.2.4}/tests/test_io.py +0 -0
  42. {tecio_python-0.2.3 → tecio_python-0.2.4}/tests/test_libtecio.py +0 -0
  43. {tecio_python-0.2.3 → tecio_python-0.2.4}/tests/test_read.py +0 -0
  44. {tecio_python-0.2.3 → tecio_python-0.2.4}/tests/test_write.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tecio-python
3
- Version: 0.2.3
3
+ Version: 0.2.4
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/
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tecio-python"
7
- version = "0.2.3"
7
+ version = "0.2.4"
8
8
  description = "Python interface for reading and writing Tecplot data files"
9
9
 
10
10
  readme = "README.md"
@@ -9,7 +9,7 @@ from importlib import metadata
9
9
  try:
10
10
  __version__ = metadata.version("tecio")
11
11
  except metadata.PackageNotFoundError:
12
- __version__ = "0.2.3"
12
+ __version__ = "0.2.4"
13
13
 
14
14
  from . import cli, dat, libtecio, plt, szl
15
15
  from ._containers import VariableList, ZoneList
@@ -137,6 +137,10 @@ _STR_TO_DATATYPE: dict[str, DataType] = {
137
137
  #: Values per line for Write data blocks.
138
138
  _VALUES_PER_LINE: int = 5
139
139
 
140
+ # Safety cap on the number of grow-and-reparse iterations the vectorized block readers
141
+ # will attempt before giving up and falling back to the tolerant token-by-token parser
142
+ _MAX_FAST_BLOCK_ITERATIONS: int = 64
143
+
140
144
 
141
145
  # ======================================================================================
142
146
  # Shared internal helpers
@@ -174,6 +178,42 @@ def _strip_comment(line: str) -> str:
174
178
  return line[:idx].rstrip() if idx >= 0 else line.rstrip()
175
179
 
176
180
 
181
+ def _is_float_block_boundary(line: str) -> bool:
182
+ r"""Return ``True`` if a stripped *line* ends a float data block early.
183
+
184
+ Shared by :meth:`Read._read_float_block_slow` (line-by-line) and
185
+ :meth:`Read._read_float_block_fast` (bulk, on parse failure) so the two readers
186
+ agree on exactly where a block stops.
187
+
188
+ Example:
189
+ >>> _is_float_block_boundary("ZONE T=\\"next\\"")
190
+ True
191
+ """
192
+ upper = line.lstrip().upper()
193
+ if not upper:
194
+ return False
195
+ if upper.split("=")[0].split()[0] == "ZONE":
196
+ return True
197
+ return (
198
+ upper.startswith(("DATASETAUXDATA", "VARAUXDATA"))
199
+ or upper.startswith("TITLE")
200
+ and "=" in upper
201
+ or upper.startswith("VARIABLES")
202
+ and "=" in upper
203
+ )
204
+
205
+
206
+ def _is_int_block_boundary(line: str) -> bool:
207
+ r"""Return ``True`` if a stripped *line* ends an int (connectivity) block.
208
+
209
+ Example:
210
+ >>> _is_int_block_boundary("DATASETAUXDATA foo=\\"bar\\"")
211
+ True
212
+ """
213
+ upper = line.lstrip().upper()
214
+ return upper.startswith(("ZONE", "DATASETAUXDATA"))
215
+
216
+
177
217
  def _infer_data_type(arr: npt.NDArray) -> DataType:
178
218
  """Return the most appropriate :class:`DataType` for *arr*'s dtype.
179
219
 
@@ -569,38 +609,99 @@ def _apply_varauxdata(line: str, var_auxdata_list: list) -> None:
569
609
  class _LineBuffer:
570
610
  """Peekable iterator over stripped, comment-free text lines.
571
611
 
612
+ Also exposes a small *raw* interface (:meth:`take_raw`, :meth:`position`,
613
+ :meth:`seek`) used only by the vectorized numeric-block readers
614
+ (:meth:`Read._read_float_block_fast`/:meth:`Read._read_int_block_fast`). Those
615
+ readers bypass per-line comment-stripping for speed and instead detect the rare line
616
+ that *isn't* plain numeric data (a comment, a header keyword) by letting NumPy's
617
+ parser fail on it, then falling back to this class's ordinary stripped-line
618
+ interface. Keeping both interfaces on one object means the fast path can hand back
619
+ an exact resume position for that fallback via :meth:`position`/:meth:`seek`.
620
+
572
621
  Example:
573
622
  >>> buf = _LineBuffer(lines)
574
623
  """
575
624
 
625
+ __slots__ = ("_lines", "_peeked", "_peeked_pos", "_pos")
626
+
576
627
  def __init__(self, lines: list[str]) -> None:
577
628
  self._lines: list[str] = lines
578
629
  self._pos: int = 0
630
+ # One-line lookahead cache so a peek_stripped() immediately followed by
631
+ # next_stripped() strips comments/whitespace only once instead of twice.
632
+ self._peeked: str | None = None
633
+ self._peeked_pos: int = -1
579
634
 
580
635
  def has_more(self) -> bool:
581
636
  """Return ``True`` if there are unconsumed lines."""
582
- return self._pos < len(self._lines)
637
+ return self._peeked is not None or self._pos < len(self._lines)
583
638
 
584
639
  def peek_stripped(self) -> str:
585
640
  """Return the next non-blank stripped line without consuming it."""
641
+ if self._peeked is not None:
642
+ return self._peeked
586
643
  pos = self._pos
587
- while pos < len(self._lines):
588
- raw = _strip_comment(self._lines[pos])
589
- stripped = raw.strip()
644
+ n = len(self._lines)
645
+ while pos < n:
646
+ stripped = _strip_comment(self._lines[pos]).strip()
647
+ pos += 1
590
648
  if stripped:
649
+ self._peeked = stripped
650
+ self._peeked_pos = pos
591
651
  return stripped
592
- pos += 1
652
+ self._peeked = ""
653
+ self._peeked_pos = pos
593
654
  return ""
594
655
 
595
656
  def next_stripped(self) -> str:
596
657
  """Consume and return the next non-blank stripped line."""
597
- while self._pos < len(self._lines):
598
- raw = _strip_comment(self._lines[self._pos])
599
- self._pos += 1
600
- stripped = raw.strip()
601
- if stripped:
602
- return stripped
603
- return ""
658
+ if self._peeked is None:
659
+ self.peek_stripped()
660
+ value = self._peeked or ""
661
+ self._pos = self._peeked_pos
662
+ self._peeked = None
663
+ self._peeked_pos = -1
664
+ return value
665
+
666
+ def take_raw(self, count: int) -> list[str]:
667
+ """Consume and return up to *count* **unprocessed** source lines.
668
+
669
+ Unlike :meth:`next_stripped`, this does not strip comments, skip blank lines, or
670
+ strip whitespace -- it is a raw slice of the underlying line list, advancing the
671
+ position by exactly how many lines were returned (fewer than *count* at end of
672
+ file). Any pending :meth:`peek_stripped` lookahead is discarded first, since
673
+ this call always resumes from the true unconsumed position.
674
+
675
+ Intended only for the numeric fast-path readers, which feed the result straight
676
+ to a whitespace-tolerant NumPy parser and don't need per-line preprocessing.
677
+
678
+ Example:
679
+ >>> lines = buf.take_raw(64)
680
+ """
681
+ self._peeked = None
682
+ self._peeked_pos = -1
683
+ end = min(self._pos + count, len(self._lines))
684
+ out = self._lines[self._pos : end]
685
+ self._pos = end
686
+ return out
687
+
688
+ def position(self) -> int:
689
+ """Return an opaque marker for the current position.
690
+
691
+ Example:
692
+ >>> marker = buf.position()
693
+ """
694
+ return self._pos
695
+
696
+ def seek(self, marker: int) -> None:
697
+ """Restore the position to a marker previously returned by :meth:`position`.
698
+
699
+ Example:
700
+ >>> buf.seek(marker)
701
+ """
702
+ self._pos = marker
703
+ self._peeked = None
704
+ self._peeked_pos = -1
604
705
 
605
706
 
606
707
  # ======================================================================================
@@ -865,7 +966,7 @@ class ReadZone:
865
966
  zone_index: int,
866
967
  title: str,
867
968
  zone_type: ZoneType,
868
- I: int, # noqa: E741
969
+ I: int, # noqa E741
869
970
  J: int,
870
971
  K: int,
871
972
  solution_time: float,
@@ -1058,7 +1159,6 @@ class Read:
1058
1159
  release on exit. Provided for API consistency with the Write classes and to
1059
1160
  support the ``with tecio.open(...) as r:`` pattern.
1060
1161
  """
1061
- pass
1062
1162
 
1063
1163
  # -- Properties --------------------------------------------------------------------
1064
1164
 
@@ -1270,9 +1370,7 @@ class Read:
1270
1370
  # Stop at a new zone, top-level keyword, or the first data line.
1271
1371
  if nxt_upper.split("=")[0].split()[0] == "ZONE":
1272
1372
  break
1273
- if nxt_upper.startswith("DATASETAUXDATA") or nxt_upper.startswith(
1274
- "VARAUXDATA"
1275
- ):
1373
+ if nxt_upper.startswith(("DATASETAUXDATA", "VARAUXDATA")):
1276
1374
  break
1277
1375
  first_ch = nxt.lstrip()[0] if nxt.lstrip() else ""
1278
1376
  # A data line begins with a numeric token (covers leading-dot values
@@ -1338,7 +1436,7 @@ class Read:
1338
1436
  )
1339
1437
 
1340
1438
  if zone_type == ZoneType.ORDERED:
1341
- I = int(kv.get("I", "1") or "1") # noqa: E741
1439
+ I = int(kv.get("I", "1") or "1") # noqa E741
1342
1440
  J = int(kv.get("J", "1") or "1")
1343
1441
  K = int(kv.get("K", "1") or "1")
1344
1442
  num_nodes = I * J * K
@@ -1348,7 +1446,7 @@ class Read:
1348
1446
  # spellings for the node and element counts.
1349
1447
  num_nodes = int(kv.get("NODES", kv.get("N", "0")) or "0")
1350
1448
  num_cells = int(kv.get("ELEMENTS", kv.get("E", "0")) or "0")
1351
- I, J, K = num_nodes, num_cells, 0 # noqa: E741
1449
+ I, J, K = num_nodes, num_cells, 0 # noqa E741
1352
1450
 
1353
1451
  # Packing: DATAPACKING wins, then legacy F, else BLOCK (Tecplot default).
1354
1452
  if "DATAPACKING" in kv:
@@ -1509,26 +1607,98 @@ class Read:
1509
1607
  """Read *n_values* floats from *tokens* into an intermediate float64 array.
1510
1608
 
1511
1609
  This is a pure text-parsing primitive. Callers cast the result to each
1512
- variable's real dtype afterward, once individual per-variable arrays have been
1610
+ variable's real dtype afterward, once individual per-variable arrays have been
1513
1611
  split out.
1514
1612
 
1613
+ Dispatches to :meth:`_read_float_block_fast`, a vectorized NumPy parser that
1614
+ handles the overwhelming majority of (well-formed) input in a handful of C-level
1615
+ calls regardless of block size, and falls back to :meth:`_read_float_block_slow`
1616
+ -- a tolerant, token-by-token parser -- only if the fast path can't make sense
1617
+ of what it read (a stray comment or non-numeric token inside the block, or a
1618
+ boundary reached before *n_values* values were collected). The fallback re-reads
1619
+ from *tokens* at the position the fast path started from, so the two never
1620
+ disagree about what was consumed.
1621
+
1515
1622
  See Also:
1516
1623
  :meth:`_read_block_var_data`/:meth:`_read_point_var_data`.
1517
1624
 
1518
1625
  Examples:
1519
1626
  >>> arr = Read._read_float_block(tokens, 100)
1520
1627
  """
1628
+ if n_values <= 0:
1629
+ return np.empty(0, dtype=np.float64)
1630
+
1631
+ marker = tokens.position()
1632
+ try:
1633
+ arr = Read._read_float_block_fast(tokens, n_values)
1634
+ except ValueError:
1635
+ arr = None
1636
+ if arr is not None:
1637
+ return arr
1638
+
1639
+ tokens.seek(marker)
1640
+ return Read._read_float_block_slow(tokens, n_values)
1641
+
1642
+ @staticmethod
1643
+ def _read_float_block_fast(
1644
+ tokens: _LineBuffer, n_values: int
1645
+ ) -> npt.NDArray | None:
1646
+ """Vectorized fast path for :meth:`_read_float_block`.
1647
+
1648
+ Reads raw (unprocessed) lines in a handful of growing bulk grabs (sized from a
1649
+ one-line width probe, so a multi-million-value block costs a small constant
1650
+ number of Python-level calls). Passing ``sep`` here is the (fully supported)
1651
+ text-parsing mode of that function, not its deprecated binary-decoding mode.
1652
+
1653
+ Returns ``None`` (never a short array) if a block boundary is hit before
1654
+ *n_values* values are collected, so the caller can unambiguously fall back to
1655
+ the slow, boundary-aware parser. A ``ValueError`` from ``numpy.fromstring`` is
1656
+ allowed to propagate to the caller for the same reason.
1657
+
1658
+ Example:
1659
+ >>> arr = Read._read_float_block_fast(tokens, 100)
1660
+
1661
+ """
1662
+ pieces: list[npt.NDArray] = []
1663
+ have = 0
1664
+ per_line: int | None = None
1665
+ for _ in range(_MAX_FAST_BLOCK_ITERATIONS):
1666
+ remaining = n_values - have
1667
+ if remaining <= 0:
1668
+ break
1669
+ # First grab is a single-line probe to measure the block's (typically
1670
+ # constant) values-per-line width
1671
+ n_lines = 1 if per_line is None else -(-remaining // per_line)
1672
+ batch = tokens.take_raw(n_lines)
1673
+ if not batch:
1674
+ return None # ran out of input before reaching n_values
1675
+ piece = np.fromstring("".join(batch), dtype=np.float64, sep=" ")
1676
+ if per_line is None:
1677
+ per_line = max(piece.size, 1)
1678
+ pieces.append(piece)
1679
+ have += piece.size
1680
+ else:
1681
+ return None # didn't converge; let the slow path sort it out
1682
+ arr = pieces[0] if len(pieces) == 1 else np.concatenate(pieces)
1683
+ return arr[:n_values] if arr.size >= n_values else None
1684
+
1685
+ @staticmethod
1686
+ def _read_float_block_slow(tokens: _LineBuffer, n_values: int) -> npt.NDArray:
1687
+ """Tolerant, token-by-token fallback for :meth:`_read_float_block`.
1688
+
1689
+ Reads one stripped line at a time, converting tokens one at a time and silently
1690
+ skipping any that aren't valid floats, stopping at a block boundary (see
1691
+ :func:`_is_float_block_boundary`) even if *n_values* hasn't been reached
1692
+ yet. Slow by design, but guarantees a result either way.
1693
+
1694
+ Example:
1695
+ >>> arr = Read._read_float_block_slow(tokens, 100)
1696
+
1697
+ """
1521
1698
  values: list[float] = []
1522
1699
  while len(values) < n_values and tokens.has_more():
1523
1700
  nxt = tokens.peek_stripped()
1524
- upper = nxt.lstrip().upper()
1525
- if (
1526
- upper.split("=")[0].split()[0] == "ZONE"
1527
- or upper.startswith("DATASETAUXDATA")
1528
- or upper.startswith("VARAUXDATA")
1529
- or (upper.startswith("TITLE") and "=" in upper)
1530
- or (upper.startswith("VARIABLES") and "=" in upper)
1531
- ):
1701
+ if _is_float_block_boundary(nxt):
1532
1702
  break
1533
1703
  line = tokens.next_stripped()
1534
1704
  for tok in line.split():
@@ -1559,6 +1729,10 @@ class Read:
1559
1729
  to that variable's actual declared dtype (from ``DT=``, or the spec's documented
1560
1730
  SINGLE default) right here, where each variable's array is finalized.
1561
1731
 
1732
+ Reading one variable at a time (rather than all of them in a single combined
1733
+ call) is deliberate: :meth:`_read_float_block`'s vectorized fast path already
1734
+ costs only a handful of C-level calls per block regardless of size.
1735
+
1562
1736
  Note:
1563
1737
  Passive and shared variables contribute a ``None`` placeholder to the
1564
1738
  returned list. The caller resolves shared variables against the zones parsed
@@ -1661,14 +1835,70 @@ class Read:
1661
1835
  def _read_int_block(tokens: _LineBuffer, n_values: int) -> npt.NDArray:
1662
1836
  """Read exactly *n_values* integers from *tokens* into an int64 array.
1663
1837
 
1838
+ Used for connectivity (node-map) data, which can run to tens of millions of
1839
+ entries for large volume meshes. Follows the same vectorized-fast-path /
1840
+ tolerant-slow-path split as :meth:`_read_float_block`; see that method's
1841
+ docstring for the rationale.
1842
+
1664
1843
  Examples:
1665
1844
  >>> arr = Read._read_int_block(tokens, 24)
1666
1845
  """
1846
+ if n_values <= 0:
1847
+ return np.empty(0, dtype=np.int64)
1848
+
1849
+ marker = tokens.position()
1850
+ try:
1851
+ arr = Read._read_int_block_fast(tokens, n_values)
1852
+ except ValueError:
1853
+ arr = None
1854
+ if arr is not None:
1855
+ return arr
1856
+
1857
+ tokens.seek(marker)
1858
+ return Read._read_int_block_slow(tokens, n_values)
1859
+
1860
+ @staticmethod
1861
+ def _read_int_block_fast(tokens: _LineBuffer, n_values: int) -> npt.NDArray | None:
1862
+ """Vectorized fast path for :meth:`_read_int_block`.
1863
+
1864
+ See :meth:`_read_float_block_fast`, which this mirrors exactly except for the
1865
+ target dtype.
1866
+
1867
+ Example:
1868
+ >>> arr = Read._read_int_block_fast(tokens, 24)
1869
+ """
1870
+ pieces: list[npt.NDArray] = []
1871
+ have = 0
1872
+ per_line: int | None = None
1873
+ for _ in range(_MAX_FAST_BLOCK_ITERATIONS):
1874
+ remaining = n_values - have
1875
+ if remaining <= 0:
1876
+ break
1877
+ n_lines = 1 if per_line is None else -(-remaining // per_line)
1878
+ batch = tokens.take_raw(n_lines)
1879
+ if not batch:
1880
+ return None
1881
+ piece = np.fromstring("".join(batch), dtype=np.int64, sep=" ")
1882
+ if per_line is None:
1883
+ per_line = max(piece.size, 1)
1884
+ pieces.append(piece)
1885
+ have += piece.size
1886
+ else:
1887
+ return None
1888
+ arr = pieces[0] if len(pieces) == 1 else np.concatenate(pieces)
1889
+ return arr[:n_values] if arr.size >= n_values else None
1890
+
1891
+ @staticmethod
1892
+ def _read_int_block_slow(tokens: _LineBuffer, n_values: int) -> npt.NDArray:
1893
+ """Tolerant, token-by-token fallback for :meth:`_read_int_block`.
1894
+
1895
+ Example:
1896
+ >>> arr = Read._read_int_block_slow(tokens, 24)
1897
+ """
1667
1898
  values: list[int] = []
1668
1899
  while len(values) < n_values and tokens.has_more():
1669
1900
  nxt = tokens.peek_stripped()
1670
- upper = nxt.lstrip().upper()
1671
- if upper.startswith("ZONE") or upper.startswith("DATASETAUXDATA"):
1901
+ if _is_int_block_boundary(nxt):
1672
1902
  break
1673
1903
  line = tokens.next_stripped()
1674
1904
  for tok in line.split():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tecio-python
3
- Version: 0.2.3
3
+ Version: 0.2.4
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/
File without changes
File without changes
File without changes
File without changes
File without changes