55from collections .abc import Generator , Sequence
66from functools import singledispatchmethod
77from pathlib import Path
8- from typing import Union , overload
8+ from typing import overload
99
1010from castep_outputs .parsers .md_geom_file_parser import (
1111 MDGeomTimestepInfo ,
@@ -29,7 +29,7 @@ class MDGeomParser:
2929 """
3030
3131 def __init__ (self , md_geom_file : Path | str ) -> None :
32- self ._next_frame = 0
32+ self ._next_frame : int | None
3333
3434 self .file = Path (md_geom_file ).expanduser ()
3535
@@ -52,12 +52,12 @@ def __init__(self, md_geom_file: Path | str) -> None:
5252
5353 while next (self ._handle ).strip ():
5454 pass
55- self ._frame_len = self ._handle .lineno - self ._start_line - 1
55+ self ._frame_lines = self ._handle .lineno - self ._start_line - 1
5656
57- self ._byte_len = self ._handle .tell () - self ._start
57+ self ._frame_bytes = self ._handle .tell () - self ._start
5858 stat = self .file .stat ()
5959
60- len_est = (stat .st_size - self ._start ) / self ._byte_len
60+ len_est = (stat .st_size - self ._start ) / self ._frame_bytes
6161
6262 if not len_est .is_integer ():
6363 self .logger (
@@ -71,10 +71,11 @@ def __init__(self, md_geom_file: Path | str) -> None:
7171 )
7272
7373 self ._len = int (len_est )
74+ self ._go_to_frame (0 )
7475
7576 @property
76- def next_frame (self ) -> int :
77- """Get index of next frame to be read."""
77+ def next_frame (self ) -> int | None :
78+ """Get index of next frame to be read, or None if at file end ."""
7879 return self ._next_frame
7980
8081 def _get_index (self , frame : int ) -> int :
@@ -90,13 +91,13 @@ def _get_index(self, frame: int) -> int:
9091 int
9192 Current index in bytes.
9293 """
93- return self ._start + (self ._byte_len * frame )
94+ return self ._start + (self ._frame_bytes * frame )
9495
9596 def _go_to_frame (self , frame : int ) -> None :
9697 """Set file pointer to given index."""
9798 ind = self ._get_index (frame )
9899 self ._handle .file .seek (ind )
99- self ._handle ._lineno = self ._start_line + (frame * self ._frame_len )
100+ self ._handle ._lineno = self ._start_line + (frame * self ._frame_lines )
100101 self ._next_frame = frame if frame < len (self ) else None
101102
102103 def get_frame (self , frame : int ) -> MDGeomTimestepInfo :
@@ -111,16 +112,20 @@ def get_frame(self, frame: int) -> MDGeomTimestepInfo:
111112 -------
112113 MDGeomTimestepInfo
113114 Parsed frame.
115+
116+ Raises
117+ ------
118+ IndexError
119+ Requested frame out of range.
114120 """
115- if - len (self ) > frame > len (self ):
116- self .logger (
117- f"Cannot get { frame } th frame. File only has { len (self )} frames." , level = "warning" ,
118- )
121+ if - len (self ) > frame < len (self ):
122+ raise IndexError (f"Cannot get { frame } th frame. File only has { len (self )} frames." )
119123
120124 if frame < 0 :
121125 frame = len (self ) + frame
122126
123- self ._go_to_frame (frame )
127+ if frame != self .next_frame :
128+ self ._go_to_frame (frame )
124129
125130 return next (self )
126131
@@ -145,13 +150,12 @@ def __iter__(self) -> Generator[MDGeomTimestepInfo, int, None]:
145150 MDGeomTimestepInfo
146151 Frames in file.
147152 """
148- self ._handle .file .seek (self ._start )
149- self ._handle ._lineno = self ._start_line
150- self ._next_frame = 0
151- while self ._next_frame is not None :
152- jump = yield next (self )
153- if jump is not None :
154- self ._go_to_frame (jump )
153+ i = 0
154+ while i < len (self ):
155+ trial = yield self [i ]
156+ i += 1
157+ if trial is not None :
158+ i = trial
155159
156160 def __next__ (self ) -> MDGeomTimestepInfo :
157161 """Get the next frame.
@@ -166,7 +170,7 @@ def __next__(self) -> MDGeomTimestepInfo:
166170 StopIteration
167171 No next frame.
168172 """
169- if not (block := Block .get_lines (self ._handle , self ._frame_len , eof_possible = True )):
173+ if not (block := Block .get_lines (self ._handle , self ._frame_lines , eof_possible = True )):
170174 raise StopIteration
171175
172176 self ._next_frame = self ._next_frame + 1 if self ._next_frame < len (self ) - 1 else None
@@ -176,7 +180,7 @@ def __next__(self) -> MDGeomTimestepInfo:
176180 def __getitem__ (self , frame : int ) -> MDGeomTimestepInfo : ...
177181
178182 @overload
179- def __getitem__ (self , frame : Union [ Sequence , slice ] ) -> list [MDGeomTimestepInfo ]: ...
183+ def __getitem__ (self , frame : Sequence | slice ) -> list [MDGeomTimestepInfo ]: ...
180184
181185 @singledispatchmethod
182186 def __getitem__ (self , frame ) -> list [MDGeomTimestepInfo ] | MDGeomTimestepInfo :
0 commit comments