Merge branch 'util_open' into 'development'

centralize opening of text files

See merge request damask/DAMASK!555
This commit is contained in:
Philip Eisenlohr 2022-03-27 17:10:31 +00:00
commit 1f98b04d44
6 changed files with 38 additions and 31 deletions

View File

@ -2,7 +2,6 @@ import os
import json import json
import functools import functools
import colorsys import colorsys
from pathlib import Path
from typing import Union, TextIO from typing import Union, TextIO
import numpy as np import numpy as np
@ -325,12 +324,7 @@ class Colormap(mpl.colors.ListedColormap):
File handle with write access. File handle with write access.
""" """
if fname is None: return util.open_text(self.name.replace(' ','_')+suffix if fname is None else fname, 'w')
return open(self.name.replace(' ','_')+suffix, 'w', newline='\n')
elif isinstance(fname, (str, Path)):
return open(Path(fname).expanduser(), 'w', newline='\n')
else:
return fname
def save_paraview(self, def save_paraview(self,

View File

@ -2,7 +2,6 @@ import copy
from io import StringIO from io import StringIO
from collections.abc import Iterable from collections.abc import Iterable
import abc import abc
from pathlib import Path
from typing import Union, Dict, Any, Type, TypeVar from typing import Union, Dict, Any, Type, TypeVar
import numpy as np import numpy as np
@ -10,6 +9,7 @@ import yaml
from ._typehints import FileHandle from ._typehints import FileHandle
from . import Rotation from . import Rotation
from . import util
MyType = TypeVar('MyType', bound='Config') MyType = TypeVar('MyType', bound='Config')
@ -144,10 +144,7 @@ class Config(dict):
Configuration from file. Configuration from file.
""" """
fhandle = open(Path(fname).expanduser(),newline='\n') if isinstance(fname, (str, Path)) else \ return cls(yaml.safe_load(util.open_text(fname)))
fname
return cls(yaml.safe_load(fhandle))
def save(self, def save(self,
fname: FileHandle, fname: FileHandle,
@ -163,9 +160,6 @@ class Config(dict):
Keyword arguments parsed to yaml.dump. Keyword arguments parsed to yaml.dump.
""" """
fhandle = open(Path(fname).expanduser(),'w',newline='\n') if isinstance(fname, (str, Path)) else \
fname
if 'width' not in kwargs: if 'width' not in kwargs:
kwargs['width'] = 256 kwargs['width'] = 256
if 'default_flow_style' not in kwargs: if 'default_flow_style' not in kwargs:
@ -173,6 +167,7 @@ class Config(dict):
if 'sort_keys' not in kwargs: if 'sort_keys' not in kwargs:
kwargs['sort_keys'] = False kwargs['sort_keys'] = False
fhandle = util.open_text(fname,'w')
try: try:
fhandle.write(yaml.dump(self,Dumper=NiceDumper,**kwargs)) fhandle.write(yaml.dump(self,Dumper=NiceDumper,**kwargs))
except TypeError: # compatibility with old pyyaml except TypeError: # compatibility with old pyyaml

View File

@ -1538,7 +1538,7 @@ class Result:
np.prod(shape))} np.prod(shape))}
data_items[-1].text = f'{os.path.split(self.fname)[1]}:{name}' data_items[-1].text = f'{os.path.split(self.fname)[1]}:{name}'
with open(self.fname.with_suffix('.xdmf').name,'w',newline='\n') as f: with util.open_text(self.fname.with_suffix('.xdmf').name,'w') as f:
f.write(xml.dom.minidom.parseString(ET.tostring(xdmf).decode()).toprettyxml()) f.write(xml.dom.minidom.parseString(ET.tostring(xdmf).decode()).toprettyxml())
@ -1803,7 +1803,7 @@ class Result:
if type(obj) == h5py.Dataset and _match(output,[name]): if type(obj) == h5py.Dataset and _match(output,[name]):
d = obj.attrs['description'] if h5py3 else obj.attrs['description'].decode() d = obj.attrs['description'] if h5py3 else obj.attrs['description'].decode()
if not Path(name).exists() or overwrite: if not Path(name).exists() or overwrite:
with open(name,'w',newline='\n') as f_out: f_out.write(obj[0].decode()) with util.open_text(name,'w') as f_out: f_out.write(obj[0].decode())
print(f'Exported {d} to "{name}".') print(f'Exported {d} to "{name}".')
else: else:
print(f'"{name}" exists, {d} not exported.') print(f'"{name}" exists, {d} not exported.')

View File

@ -1,6 +1,5 @@
import re import re
import copy import copy
from pathlib import Path
from typing import Union, Tuple, List from typing import Union, Tuple, List
import pandas as pd import pandas as pd
@ -260,7 +259,7 @@ class Table:
Table data from file. Table data from file.
""" """
f = open(Path(fname).expanduser(),newline='\n') if isinstance(fname, (str, Path)) else fname f = util.open_text(fname)
f.seek(0) f.seek(0)
comments = [] comments = []
@ -281,7 +280,7 @@ class Table:
else: else:
shapes[label] = (1,) shapes[label] = (1,)
data = pd.read_csv(f,names=list(range(len(labels))),sep=r'\s+',lineterminator='\n') data = pd.read_csv(f,names=list(range(len(labels))),sep=r'\s+')
return Table(shapes,data,comments) return Table(shapes,data,comments)
@ -312,7 +311,7 @@ class Table:
Table data from file. Table data from file.
""" """
f = open(Path(fname).expanduser(),newline='\n') if isinstance(fname, (str, Path)) else fname f = util.open_text(fname)
f.seek(0) f.seek(0)
content = f.readlines() content = f.readlines()
@ -594,7 +593,7 @@ class Table:
labels += [f'{util.srepr(self.shapes[l],"x")}:{i+1}_{l}' \ labels += [f'{util.srepr(self.shapes[l],"x")}:{i+1}_{l}' \
for i in range(np.prod(self.shapes[l]))] for i in range(np.prod(self.shapes[l]))]
f = open(Path(fname).expanduser(),'w',newline='\n') if isinstance(fname, (str, Path)) else fname f = util.open_text(fname,'w')
f.write('\n'.join([f'# {c}' for c in self.comments] + [' '.join(labels)])+('\n' if labels else '')) f.write('\n'.join([f'# {c}' for c in self.comments] + [' '.join(labels)])+('\n' if labels else ''))
self.data.to_csv(f,sep=' ',na_rep='nan',index=False,header=False,line_terminator='\n') self.data.to_csv(f,sep=' ',na_rep='nan',index=False,header=False,line_terminator='\n')

View File

@ -10,20 +10,21 @@ import signal
import fractions import fractions
from collections import abc from collections import abc
from functools import reduce, partial from functools import reduce, partial
from typing import Callable, Union, Iterable, Sequence, Dict, List, Tuple, Literal, Any, Collection from typing import Callable, Union, Iterable, Sequence, Dict, List, Tuple, Literal, Any, Collection, TextIO
from pathlib import Path from pathlib import Path
import numpy as np import numpy as np
import h5py import h5py
from . import version from . import version
from ._typehints import FloatSequence, NumpyRngSeed, IntCollection from ._typehints import FloatSequence, NumpyRngSeed, IntCollection, FileHandle
# limit visibility # limit visibility
__all__=[ __all__=[
'srepr', 'srepr',
'emph', 'deemph', 'warn', 'strikeout', 'emph', 'deemph', 'warn', 'strikeout',
'run', 'run',
'open_text',
'natural_sort', 'natural_sort',
'show_progress', 'show_progress',
'scale_to_coprime', 'scale_to_coprime',
@ -206,7 +207,25 @@ def run(cmd: str,
return stdout, stderr return stdout, stderr
execute = run def open_text(fname: FileHandle,
mode: Literal['r','w'] = 'r') -> TextIO:
"""
Open a text file.
Parameters
----------
fname : file, str, or pathlib.Path
Name or handle of file.
mode: {'r','w'}, optional
Access mode: 'r'ead or 'w'rite, defaults to 'r'.
Returns
-------
f : file handle
"""
return fname if not isinstance(fname, (str,Path)) else \
open(Path(fname).expanduser(),mode,newline=('\n' if mode == 'w' else None))
def natural_sort(key: str) -> List[Union[int, str]]: def natural_sort(key: str) -> List[Union[int, str]]:

View File

@ -12,19 +12,19 @@ from damask import util
class TestUtil: class TestUtil:
@pytest.mark.xfail(sys.platform == 'win32', reason='echo is not a Windows command') @pytest.mark.xfail(sys.platform == 'win32', reason='echo is not a Windows command')
def test_execute_direct(self): def test_run_direct(self):
out,err = util.execute('echo test') out,err = util.run('echo test')
assert out=='test\n' and err=='' assert out=='test\n' and err==''
@pytest.mark.xfail(sys.platform == 'win32', reason='echo is not a Windows command') @pytest.mark.xfail(sys.platform == 'win32', reason='echo is not a Windows command')
def test_execute_env(self): def test_run_env(self):
out,err = util.execute('sh -c "echo $test_for_execute"',env={'test_for_execute':'test'}) out,err = util.run('sh -c "echo $test_for_execute"',env={'test_for_execute':'test'})
assert out=='test\n' and err=='' assert out=='test\n' and err==''
@pytest.mark.xfail(sys.platform == 'win32', reason='false is not a Windows command') @pytest.mark.xfail(sys.platform == 'win32', reason='false is not a Windows command')
def test_execute_runtime_error(self): def test_run_runtime_error(self):
with pytest.raises(RuntimeError): with pytest.raises(RuntimeError):
util.execute('false') util.run('false')
@pytest.mark.parametrize('input,output', @pytest.mark.parametrize('input,output',
[ [