@@ -65,32 +65,34 @@ def load(cls, fh, mode=AUTOMATIC, speedups=True):
6565 if isinstance (header , str ): # pragma: no branch
6666 header = b (header )
6767
68- name = ''
69-
70- if mode in (AUTOMATIC , ASCII ) and header [:5 ].lower () == b ('solid' ):
71- try :
72- name , data = cls ._load_ascii (
73- fh , header , speedups = speedups )
74- except RuntimeError as exception :
75- # Disable fallbacks in ASCII mode
76- if mode is ASCII :
77- raise
78-
79- (recoverable , e ) = exception .args
80- # If we didn't read beyond the header the stream is still
81- # readable through the binary reader
82- if recoverable :
83- name , data = cls ._load_binary (fh , header , check_size = False )
84- else :
85- # Apparently we've read beyond the header. Let's try
86- # seeking :)
87- # Note that this fails when reading from stdin, we can't
88- # recover from that.
89- fh .seek (HEADER_SIZE )
90-
91- # Since we know this is a seekable file now and we're not
92- # 100% certain it's binary, check the size while reading
93- name , data = cls ._load_binary (fh , header , check_size = True )
68+ if mode is AUTOMATIC :
69+ if header .lstrip ().lower ().startswith (b'solid' ):
70+ try :
71+ name , data = cls ._load_ascii (
72+ fh , header , speedups = speedups )
73+ except RuntimeError as exception :
74+ (recoverable , e ) = exception .args
75+ # If we didn't read beyond the header the stream is still
76+ # readable through the binary reader
77+ if recoverable :
78+ name , data = cls ._load_binary (fh , header ,
79+ check_size = False )
80+ else :
81+ # Apparently we've read beyond the header. Let's try
82+ # seeking :)
83+ # Note that this fails when reading from stdin, we
84+ # can't recover from that.
85+ fh .seek (HEADER_SIZE )
86+
87+ # Since we know this is a seekable file now and we're
88+ # not 100% certain it's binary, check the size while
89+ # reading
90+ name , data = cls ._load_binary (fh , header ,
91+ check_size = True )
92+ else :
93+ name , data = cls ._load_binary (fh , header )
94+ elif mode is ASCII :
95+ name , data = cls ._load_ascii (fh , header , speedups = speedups )
9496 else :
9597 name , data = cls ._load_binary (fh , header )
9698
@@ -231,6 +233,7 @@ def _load_ascii(cls, fh, header, speedups=True):
231233 else :
232234 iterator = cls ._ascii_reader (fh , header )
233235 name = next (iterator )
236+ print ('got name' , name )
234237 return name , numpy .fromiter (iterator , dtype = cls .dtype )
235238
236239 def save (self , filename , fh = None , mode = AUTOMATIC , update_normals = True ):
@@ -249,8 +252,17 @@ def save(self, filename, fh=None, mode=AUTOMATIC, update_normals=True):
249252 self .update_normals ()
250253
251254 if mode is AUTOMATIC :
252- if fh and os .isatty (fh .fileno ()): # pragma: no cover
253- write = self ._write_ascii
255+ # Try to determine if the file is a TTY.
256+ if fh :
257+ try :
258+ if os .isatty (fh .fileno ()): # pragma: no cover
259+ write = self ._write_ascii
260+ else :
261+ write = self ._write_binary
262+ except IOError :
263+ # If TTY checking fails then it's an io.BytesIO() (or one
264+ # of its siblings from io). Assume binary.
265+ write = self ._write_binary
254266 else :
255267 write = self ._write_binary
256268 elif mode is BINARY :
@@ -260,6 +272,13 @@ def save(self, filename, fh=None, mode=AUTOMATIC, update_normals=True):
260272 else :
261273 raise ValueError ('Mode %r is invalid' % mode )
262274
275+ if isinstance (fh , io .TextIOBase ):
276+ # Provide a more helpful error if the user mistakenly
277+ # assumes ASCII files should be text files.
278+ raise TypeError (
279+ "File handles should be in binary mode - even when"
280+ " writing an ASCII STL." )
281+
263282 name = os .path .split (filename )[- 1 ]
264283 try :
265284 if fh :
@@ -271,7 +290,13 @@ def save(self, filename, fh=None, mode=AUTOMATIC, update_normals=True):
271290 pass
272291
273292 def _write_ascii (self , fh , name ):
274- if _speedups and self .speedups : # pragma: no cover
293+ try :
294+ fh .fileno ()
295+ speedups = self .speedups
296+ except io .UnsupportedOperation :
297+ speedups = False
298+
299+ if _speedups and speedups : # pragma: no cover
275300 _speedups .ascii_write (fh , b (name ), self .data )
276301 else :
277302 def p (s , file ):
@@ -315,8 +340,16 @@ def _write_binary(self, fh, name):
315340
316341 fh .write (header )
317342 fh .write (packed )
318- self .data .tofile (fh )
319343
344+ if isinstance (fh , io .BufferedWriter ):
345+ # Write to a true file.
346+ self .data .tofile (fh )
347+ else :
348+ # Write to a pseudo buffer.
349+ fh .write (self .data .data )
350+
351+ # In theory this should no longer be possible but I'll leave it here
352+ # anyway...
320353 if self .data .size : # pragma: no cover
321354 assert fh .tell () > 84 , (
322355 'numpy silently refused to write our file. Note that writing '
@@ -403,4 +436,3 @@ def from_files(cls, filenames, calculate_normals=True, mode=Mode.AUTOMATIC,
403436
404437
405438StlMesh = BaseStl .from_file
406-
0 commit comments