From ffa80f6bef02525b3f1a9477c3a4460e04a46df3 Mon Sep 17 00:00:00 2001 From: Daniel Otto de Mentock Date: Wed, 12 Jan 2022 17:10:13 +0100 Subject: [PATCH 1/7] added typehints for table module --- python/damask/_table.py | 86 ++++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 35 deletions(-) diff --git a/python/damask/_table.py b/python/damask/_table.py index a65971f39..04ab426ce 100644 --- a/python/damask/_table.py +++ b/python/damask/_table.py @@ -1,5 +1,7 @@ import re import copy +from typing import Union, Optional, Tuple, List, TextIO, Set +import pathlib import pandas as pd import numpy as np @@ -9,7 +11,7 @@ from . import util class Table: """Manipulate multi-dimensional spreadsheet-like data.""" - def __init__(self,data,shapes,comments=None): + def __init__(self, data: np.ndarray, shapes: dict, comments: Optional[Union[str, list]] = None): """ New spreadsheet. @@ -30,7 +32,7 @@ class Table: self._relabel('uniform') - def __repr__(self): + def __repr__(self) -> str: """Brief overview.""" self._relabel('shapes') data_repr = self.data.__repr__() @@ -38,7 +40,7 @@ class Table: return '\n'.join(['# '+c for c in self.comments])+'\n'+data_repr - def __getitem__(self,item): + def __getitem__(self, item: Union[slice, Tuple[slice, ...]]) -> "Table": """ Slice the Table according to item. @@ -85,19 +87,19 @@ class Table: comments=self.comments) - def __len__(self): + def __len__(self) -> int: """Number of rows.""" return len(self.data) - def __copy__(self): + def __copy__(self) -> "Table": """Create deep copy.""" return copy.deepcopy(self) copy = __copy__ - def _label(self,what,how): + def _label(self, what: Union[str, List[str]], how: str) -> List[str]: """ Expand labels according to data shape. @@ -128,7 +130,7 @@ class Table: return labels - def _relabel(self,how): + def _relabel(self, how: str): """ Modify labeling of data in-place. @@ -141,17 +143,21 @@ class Table: 'linear' ==> 1_v 2_v 3_v """ - self.data.columns = self._label(self.shapes,how) + self.data.columns = self._label(self.shapes,how) #type: ignore - def _add_comment(self,label,shape,info): + def _add_comment(self, label: str, shape: Tuple[int, ...], info: Optional[str]): if info is not None: specific = f'{label}{" "+str(shape) if np.prod(shape,dtype=int) > 1 else ""}: {info}' general = util.execution_stamp('Table') self.comments.append(f'{specific} / {general}') - def isclose(self,other,rtol=1e-5,atol=1e-8,equal_nan=True): + def isclose(self, + other: "Table", + rtol: float = 1e-5, + atol: float = 1e-8, + equal_nan: bool = True) -> np.ndarray: """ Report where values are approximately equal to corresponding ones of other Table. @@ -179,7 +185,11 @@ class Table: equal_nan=equal_nan) - def allclose(self,other,rtol=1e-5,atol=1e-8,equal_nan=True): + def allclose(self, + other: "Table", + rtol: float = 1e-5, + atol: float = 1e-8, + equal_nan: bool = True) -> bool: """ Test whether all values are approximately equal to corresponding ones of other Table. @@ -208,7 +218,7 @@ class Table: @staticmethod - def load(fname): + def load(fname: Union[TextIO, str, pathlib.Path]) -> "Table": """ Load from ASCII table file. @@ -229,11 +239,13 @@ class Table: Table data from file. """ - try: - f = open(fname) - except TypeError: + if isinstance(fname, TextIO): f = fname f.seek(0) + elif isinstance(fname, (str, pathlib.Path)): + f = open(fname) + else: + raise TypeError comments = [] line = f.readline().strip() @@ -261,7 +273,7 @@ class Table: @staticmethod - def load_ang(fname): + def load_ang(fname: Union[TextIO, str, pathlib.Path]) -> "Table": """ Load from ang file. @@ -286,11 +298,13 @@ class Table: Table data from file. """ - try: - f = open(fname) - except TypeError: + if isinstance(fname, TextIO): f = fname f.seek(0) + elif isinstance(fname, (str, pathlib.Path)): + f = open(fname) + else: + raise TypeError content = f.readlines() @@ -312,11 +326,11 @@ class Table: @property - def labels(self): + def labels(self) -> List[Tuple[int, ...]]: return list(self.shapes) - def get(self,label): + def get(self, label: str) -> np.ndarray: """ Get column data. @@ -336,7 +350,7 @@ class Table: return data.astype(type(data.flatten()[0])) - def set(self,label,data,info=None): + def set(self, label: str, data: np.ndarray, info: str = None) -> "Table": """ Set column data. @@ -356,7 +370,7 @@ class Table: """ dup = self.copy() - dup._add_comment(label,data.shape[1:],info) + dup._add_comment(label, data.shape[1:],info) m = re.match(r'(.*)\[((\d+,)*(\d+))\]',label) if m: key = m.group(1) @@ -369,7 +383,7 @@ class Table: return dup - def add(self,label,data,info=None): + def add(self, label: str, data: np.ndarray, info: str = None) -> "Table": """ Add column data. @@ -401,7 +415,7 @@ class Table: return dup - def delete(self,label): + def delete(self, label: str) -> "Table": """ Delete column data. @@ -422,7 +436,7 @@ class Table: return dup - def rename(self,old,new,info=None): + def rename(self, old: Union[str, List[str]], new: Union[str, List[str]], info: str = None) -> "Table": """ Rename column data. @@ -448,7 +462,7 @@ class Table: return dup - def sort_by(self,labels,ascending=True): + def sort_by(self, labels: Union[str, List[str]], ascending: Union[bool, List[bool]] = True) -> "Table": """ Sort table by values of given labels. @@ -481,7 +495,7 @@ class Table: return dup - def append(self,other): + def append(self, other: "Table") -> "Table": """ Append other table vertically (similar to numpy.vstack). @@ -506,7 +520,7 @@ class Table: return dup - def join(self,other): + def join(self, other: "Table") -> "Table": """ Append other table horizontally (similar to numpy.hstack). @@ -533,7 +547,7 @@ class Table: return dup - def save(self,fname): + def save(self, fname: Union[TextIO, str, pathlib.Path]): """ Save as plain text file. @@ -543,9 +557,9 @@ class Table: Filename or file for writing. """ - seen = set() + seen: Set = set() labels = [] - for l in [x for x in self.data.columns if not (x in seen or seen.add(x))]: + for l in [x for x in self.data.columns if x not in seen]: if self.shapes[l] == (1,): labels.append(f'{l}') elif len(self.shapes[l]) == 1: @@ -555,10 +569,12 @@ class Table: labels += [f'{util.srepr(self.shapes[l],"x")}:{i+1}_{l}' \ for i in range(np.prod(self.shapes[l]))] - try: - fhandle = open(fname,'w',newline='\n') - except TypeError: + if isinstance(fname, TextIO): fhandle = fname + elif isinstance(fname, (str, pathlib.Path)): + fhandle = open(fname,'w',newline='\n') + else: + raise TypeError fhandle.write('\n'.join([f'# {c}' for c in self.comments] + [' '.join(labels)])+'\n') self.data.to_csv(fhandle,sep=' ',na_rep='nan',index=False,header=False) From b4088b666e5c8031dab9a66967a765deb7ff856b Mon Sep 17 00:00:00 2001 From: Daniel Otto de Mentock Date: Thu, 13 Jan 2022 13:27:50 +0100 Subject: [PATCH 2/7] adjusted filehandle conditional type --- python/damask/_table.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/python/damask/_table.py b/python/damask/_table.py index 04ab426ce..ed7c80460 100644 --- a/python/damask/_table.py +++ b/python/damask/_table.py @@ -1,13 +1,15 @@ import re import copy -from typing import Union, Optional, Tuple, List, TextIO, Set import pathlib +from typing import Union, Optional, Tuple, List, TextIO, Set +from _io import TextIOWrapper import pandas as pd import numpy as np from . import util + class Table: """Manipulate multi-dimensional spreadsheet-like data.""" @@ -239,7 +241,7 @@ class Table: Table data from file. """ - if isinstance(fname, TextIO): + if isinstance(fname, TextIOWrapper): f = fname f.seek(0) elif isinstance(fname, (str, pathlib.Path)): @@ -298,7 +300,7 @@ class Table: Table data from file. """ - if isinstance(fname, TextIO): + if isinstance(fname, TextIOWrapper): f = fname f.seek(0) elif isinstance(fname, (str, pathlib.Path)): @@ -569,7 +571,7 @@ class Table: labels += [f'{util.srepr(self.shapes[l],"x")}:{i+1}_{l}' \ for i in range(np.prod(self.shapes[l]))] - if isinstance(fname, TextIO): + if isinstance(fname, TextIOWrapper): fhandle = fname elif isinstance(fname, (str, pathlib.Path)): fhandle = open(fname,'w',newline='\n') From db21e82fe3315bfc0f744099437a864277e3e8ca Mon Sep 17 00:00:00 2001 From: Daniel Otto de Mentock Date: Thu, 13 Jan 2022 17:15:54 +0100 Subject: [PATCH 3/7] adjusted data.column retrieval in save method --- python/damask/_table.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/python/damask/_table.py b/python/damask/_table.py index ed7c80460..0610e3140 100644 --- a/python/damask/_table.py +++ b/python/damask/_table.py @@ -1,7 +1,7 @@ import re import copy import pathlib -from typing import Union, Optional, Tuple, List, TextIO, Set +from typing import Union, Optional, Tuple, List, TextIO from _io import TextIOWrapper import pandas as pd @@ -9,7 +9,6 @@ import numpy as np from . import util - class Table: """Manipulate multi-dimensional spreadsheet-like data.""" @@ -372,7 +371,7 @@ class Table: """ dup = self.copy() - dup._add_comment(label, data.shape[1:],info) + dup._add_comment(label, data.shape[1:], info) m = re.match(r'(.*)\[((\d+,)*(\d+))\]',label) if m: key = m.group(1) @@ -559,9 +558,12 @@ class Table: Filename or file for writing. """ - seen: Set = set() + data_column_items = [] + for col in self.data.columns: + if col not in data_column_items: + data_column_items.append(col) labels = [] - for l in [x for x in self.data.columns if x not in seen]: + for l in data_column_items: if self.shapes[l] == (1,): labels.append(f'{l}') elif len(self.shapes[l]) == 1: From dd82c3c8f07add648ddac84c9295dbd94704ee38 Mon Sep 17 00:00:00 2001 From: Daniel Otto de Mentock Date: Fri, 14 Jan 2022 15:08:40 +0100 Subject: [PATCH 4/7] rewrote table module adjustments to not make use of _io module --- python/damask/_table.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/python/damask/_table.py b/python/damask/_table.py index 0610e3140..2221ab318 100644 --- a/python/damask/_table.py +++ b/python/damask/_table.py @@ -2,7 +2,6 @@ import re import copy import pathlib from typing import Union, Optional, Tuple, List, TextIO -from _io import TextIOWrapper import pandas as pd import numpy as np @@ -238,15 +237,13 @@ class Table: ------- loaded : damask.Table Table data from file. - + """ - if isinstance(fname, TextIOWrapper): - f = fname - f.seek(0) - elif isinstance(fname, (str, pathlib.Path)): + if isinstance(fname, (str, pathlib.Path)): f = open(fname) else: - raise TypeError + f = fname + f.seek(0) comments = [] line = f.readline().strip() @@ -299,13 +296,11 @@ class Table: Table data from file. """ - if isinstance(fname, TextIOWrapper): - f = fname - f.seek(0) - elif isinstance(fname, (str, pathlib.Path)): + if isinstance(fname, (str, pathlib.Path)): f = open(fname) else: - raise TypeError + f = fname + f.seek(0) content = f.readlines() @@ -573,12 +568,10 @@ class Table: labels += [f'{util.srepr(self.shapes[l],"x")}:{i+1}_{l}' \ for i in range(np.prod(self.shapes[l]))] - if isinstance(fname, TextIOWrapper): - fhandle = fname - elif isinstance(fname, (str, pathlib.Path)): + if isinstance(fname, (str, pathlib.Path)): fhandle = open(fname,'w',newline='\n') else: - raise TypeError + fhandle = fname fhandle.write('\n'.join([f'# {c}' for c in self.comments] + [' '.join(labels)])+'\n') self.data.to_csv(fhandle,sep=' ',na_rep='nan',index=False,header=False) From 3e584570b287f9c98e2aaf68922f8cae859ed20b Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 23 Jan 2022 14:15:25 +0100 Subject: [PATCH 5/7] polishing --- python/damask/_table.py | 58 ++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/python/damask/_table.py b/python/damask/_table.py index 2221ab318..418549cbb 100644 --- a/python/damask/_table.py +++ b/python/damask/_table.py @@ -1,11 +1,12 @@ import re import copy -import pathlib -from typing import Union, Optional, Tuple, List, TextIO +from pathlib import Path +from typing import Union, Optional, Tuple, List import pandas as pd import numpy as np +from ._typehints import FileHandle from . import util class Table: @@ -40,7 +41,7 @@ class Table: return '\n'.join(['# '+c for c in self.comments])+'\n'+data_repr - def __getitem__(self, item: Union[slice, Tuple[slice, ...]]) -> "Table": + def __getitem__(self, item: Union[slice, Tuple[slice, ...]]) -> 'Table': """ Slice the Table according to item. @@ -92,7 +93,7 @@ class Table: return len(self.data) - def __copy__(self) -> "Table": + def __copy__(self) -> 'Table': """Create deep copy.""" return copy.deepcopy(self) @@ -154,7 +155,7 @@ class Table: def isclose(self, - other: "Table", + other: 'Table', rtol: float = 1e-5, atol: float = 1e-8, equal_nan: bool = True) -> np.ndarray: @@ -186,7 +187,7 @@ class Table: def allclose(self, - other: "Table", + other: 'Table', rtol: float = 1e-5, atol: float = 1e-8, equal_nan: bool = True) -> bool: @@ -218,7 +219,7 @@ class Table: @staticmethod - def load(fname: Union[TextIO, str, pathlib.Path]) -> "Table": + def load(fname: FileHandle) -> 'Table': """ Load from ASCII table file. @@ -237,13 +238,10 @@ class Table: ------- loaded : damask.Table Table data from file. - + """ - if isinstance(fname, (str, pathlib.Path)): - f = open(fname) - else: - f = fname - f.seek(0) + f = open(fname) if isinstance(fname, (str, Path)) else fname + f.seek(0) comments = [] line = f.readline().strip() @@ -271,7 +269,7 @@ class Table: @staticmethod - def load_ang(fname: Union[TextIO, str, pathlib.Path]) -> "Table": + def load_ang(fname: FileHandle) -> 'Table': """ Load from ang file. @@ -296,11 +294,8 @@ class Table: Table data from file. """ - if isinstance(fname, (str, pathlib.Path)): - f = open(fname) - else: - f = fname - f.seek(0) + f = open(fname) if isinstance(fname, (str, Path)) else fname + f.seek(0) content = f.readlines() @@ -346,7 +341,7 @@ class Table: return data.astype(type(data.flatten()[0])) - def set(self, label: str, data: np.ndarray, info: str = None) -> "Table": + def set(self, label: str, data: np.ndarray, info: str = None) -> 'Table': """ Set column data. @@ -379,7 +374,7 @@ class Table: return dup - def add(self, label: str, data: np.ndarray, info: str = None) -> "Table": + def add(self, label: str, data: np.ndarray, info: str = None) -> 'Table': """ Add column data. @@ -411,7 +406,7 @@ class Table: return dup - def delete(self, label: str) -> "Table": + def delete(self, label: str) -> 'Table': """ Delete column data. @@ -432,7 +427,7 @@ class Table: return dup - def rename(self, old: Union[str, List[str]], new: Union[str, List[str]], info: str = None) -> "Table": + def rename(self, old: Union[str, List[str]], new: Union[str, List[str]], info: str = None) -> 'Table': """ Rename column data. @@ -458,7 +453,7 @@ class Table: return dup - def sort_by(self, labels: Union[str, List[str]], ascending: Union[bool, List[bool]] = True) -> "Table": + def sort_by(self, labels: Union[str, List[str]], ascending: Union[bool, List[bool]] = True) -> 'Table': """ Sort table by values of given labels. @@ -491,7 +486,7 @@ class Table: return dup - def append(self, other: "Table") -> "Table": + def append(self, other: 'Table') -> 'Table': """ Append other table vertically (similar to numpy.vstack). @@ -516,7 +511,7 @@ class Table: return dup - def join(self, other: "Table") -> "Table": + def join(self, other: 'Table') -> 'Table': """ Append other table horizontally (similar to numpy.hstack). @@ -543,7 +538,7 @@ class Table: return dup - def save(self, fname: Union[TextIO, str, pathlib.Path]): + def save(self, fname: FileHandle): """ Save as plain text file. @@ -568,10 +563,7 @@ class Table: labels += [f'{util.srepr(self.shapes[l],"x")}:{i+1}_{l}' \ for i in range(np.prod(self.shapes[l]))] - if isinstance(fname, (str, pathlib.Path)): - fhandle = open(fname,'w',newline='\n') - else: - fhandle = fname + f = open(fname,'w',newline='\n') if isinstance(fname, (str, Path)) else fname - fhandle.write('\n'.join([f'# {c}' for c in self.comments] + [' '.join(labels)])+'\n') - self.data.to_csv(fhandle,sep=' ',na_rep='nan',index=False,header=False) + f.write('\n'.join([f'# {c}' for c in self.comments] + [' '.join(labels)])+'\n') + self.data.to_csv(f,sep=' ',na_rep='nan',index=False,header=False) From e320623a40c30b611eaa2907884e337d360c4d19 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 23 Jan 2022 14:22:36 +0100 Subject: [PATCH 6/7] simplified dict keeps order since Python 3.6 --- python/damask/_table.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/python/damask/_table.py b/python/damask/_table.py index 418549cbb..68ed869c3 100644 --- a/python/damask/_table.py +++ b/python/damask/_table.py @@ -548,12 +548,8 @@ class Table: Filename or file for writing. """ - data_column_items = [] - for col in self.data.columns: - if col not in data_column_items: - data_column_items.append(col) labels = [] - for l in data_column_items: + for l in list(dict.fromkeys(self.data.columns)): if self.shapes[l] == (1,): labels.append(f'{l}') elif len(self.shapes[l]) == 1: From 2f07d88bc7fbd8ed12b7deca259822030adf7ea3 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Mon, 24 Jan 2022 21:09:13 +0000 Subject: [PATCH 7/7] clarified set of option values --- python/damask/_table.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/python/damask/_table.py b/python/damask/_table.py index 68ed869c3..fed309439 100644 --- a/python/damask/_table.py +++ b/python/damask/_table.py @@ -108,7 +108,7 @@ class Table: ---------- what : str or list Labels to expand. - how : str + how : {'uniform, 'shapes', 'linear'} Mode of labeling. 'uniform' ==> v v v 'shapes' ==> 3:v v v @@ -137,7 +137,7 @@ class Table: Parameters ---------- - how : str + how : {'uniform, 'shapes', 'linear'} Mode of labeling. 'uniform' ==> v v v 'shapes' ==> 3:v v v @@ -155,10 +155,10 @@ class Table: def isclose(self, - other: 'Table', - rtol: float = 1e-5, - atol: float = 1e-8, - equal_nan: bool = True) -> np.ndarray: + other: 'Table', + rtol: float = 1e-5, + atol: float = 1e-8, + equal_nan: bool = True) -> np.ndarray: """ Report where values are approximately equal to corresponding ones of other Table.