avoid code duplication and inconsistencies

This commit is contained in:
Martin Diehl 2022-03-27 09:03:51 +02:00
parent 2dfde6997b
commit f3cf67d3fa
6 changed files with 36 additions and 30 deletions

View File

@ -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,

View File

@ -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

View File

@ -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.')

View File

@ -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')

View File

@ -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]]:

View File

@ -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',
[