diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index b4d184786..5253e0acb 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -2,7 +2,6 @@ import os import json import functools import colorsys -from pathlib import Path from typing import Union, TextIO import numpy as np @@ -325,12 +324,7 @@ class Colormap(mpl.colors.ListedColormap): File handle with write access. """ - if fname is None: - 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 + return util.open_text(self.name.replace(' ','_')+suffix if fname is None else fname, 'w') def save_paraview(self, diff --git a/python/damask/_config.py b/python/damask/_config.py index 671f203a8..6dd6a8328 100644 --- a/python/damask/_config.py +++ b/python/damask/_config.py @@ -2,7 +2,6 @@ import copy from io import StringIO from collections.abc import Iterable import abc -from pathlib import Path from typing import Union, Dict, Any, Type, TypeVar import numpy as np @@ -10,6 +9,7 @@ import yaml from ._typehints import FileHandle from . import Rotation +from . import util MyType = TypeVar('MyType', bound='Config') @@ -144,10 +144,7 @@ class Config(dict): Configuration from file. """ - fhandle = open(Path(fname).expanduser(),newline='\n') if isinstance(fname, (str, Path)) else \ - fname - - return cls(yaml.safe_load(fhandle)) + return cls(yaml.safe_load(util.open_text(fname))) def save(self, fname: FileHandle, @@ -163,9 +160,6 @@ class Config(dict): 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: kwargs['width'] = 256 if 'default_flow_style' not in kwargs: @@ -173,6 +167,7 @@ class Config(dict): if 'sort_keys' not in kwargs: kwargs['sort_keys'] = False + fhandle = util.open_text(fname,'w') try: fhandle.write(yaml.dump(self,Dumper=NiceDumper,**kwargs)) except TypeError: # compatibility with old pyyaml diff --git a/python/damask/_result.py b/python/damask/_result.py index 0cc2008aa..f3b649a30 100644 --- a/python/damask/_result.py +++ b/python/damask/_result.py @@ -1538,7 +1538,7 @@ class Result: np.prod(shape))} 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()) @@ -1803,7 +1803,7 @@ class Result: if type(obj) == h5py.Dataset and _match(output,[name]): d = obj.attrs['description'] if h5py3 else obj.attrs['description'].decode() 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}".') else: print(f'"{name}" exists, {d} not exported.') diff --git a/python/damask/_table.py b/python/damask/_table.py index d8f4d5c6b..a00965c22 100644 --- a/python/damask/_table.py +++ b/python/damask/_table.py @@ -1,6 +1,5 @@ import re import copy -from pathlib import Path from typing import Union, Tuple, List import pandas as pd @@ -260,7 +259,7 @@ class Table: 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) comments = [] @@ -312,7 +311,7 @@ class Table: 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) content = f.readlines() @@ -594,7 +593,7 @@ class Table: labels += [f'{util.srepr(self.shapes[l],"x")}:{i+1}_{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 '')) self.data.to_csv(f,sep=' ',na_rep='nan',index=False,header=False,line_terminator='\n') diff --git a/python/damask/util.py b/python/damask/util.py index e7e75718d..7b7582f7b 100644 --- a/python/damask/util.py +++ b/python/damask/util.py @@ -10,20 +10,21 @@ import signal import fractions from collections import abc 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 import numpy as np import h5py from . import version -from ._typehints import FloatSequence, NumpyRngSeed, IntCollection +from ._typehints import FloatSequence, NumpyRngSeed, IntCollection, FileHandle # limit visibility __all__=[ 'srepr', 'emph', 'deemph', 'warn', 'strikeout', 'run', + 'open_text', 'natural_sort', 'show_progress', 'scale_to_coprime', @@ -206,7 +207,24 @@ def run(cmd: str, 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 open(Path(fname).expanduser(),mode,newline='\n') if isinstance(fname, (str, Path)) else fname def natural_sort(key: str) -> List[Union[int, str]]: diff --git a/python/tests/test_util.py b/python/tests/test_util.py index ec547e6d2..80786249a 100644 --- a/python/tests/test_util.py +++ b/python/tests/test_util.py @@ -12,19 +12,19 @@ from damask import util class TestUtil: @pytest.mark.xfail(sys.platform == 'win32', reason='echo is not a Windows command') - def test_execute_direct(self): - out,err = util.execute('echo test') + def test_run_direct(self): + out,err = util.run('echo test') assert out=='test\n' and err=='' @pytest.mark.xfail(sys.platform == 'win32', reason='echo is not a Windows command') - def test_execute_env(self): - out,err = util.execute('sh -c "echo $test_for_execute"',env={'test_for_execute':'test'}) + def test_run_env(self): + out,err = util.run('sh -c "echo $test_for_execute"',env={'test_for_execute':'test'}) assert out=='test\n' and err=='' @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): - util.execute('false') + util.run('false') @pytest.mark.parametrize('input,output', [