mtmhdf 0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mtmhdf/__init__.py +1 -0
- mtmhdf/_hdf4.py +53 -0
- mtmhdf/_hdf5.py +52 -0
- mtmhdf/_utils.py +15 -0
- mtmhdf/reader.py +214 -0
- mtmhdf-0.0.2.dist-info/METADATA +43 -0
- mtmhdf-0.0.2.dist-info/RECORD +10 -0
- mtmhdf-0.0.2.dist-info/WHEEL +5 -0
- mtmhdf-0.0.2.dist-info/licenses/LICENSE +21 -0
- mtmhdf-0.0.2.dist-info/top_level.txt +1 -0
mtmhdf/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .reader import Reader
|
mtmhdf/_hdf4.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from pyhdf.SD import SD, SDC, SDS
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class HDF4:
|
|
9
|
+
DATATYPES = {
|
|
10
|
+
4: "char",
|
|
11
|
+
3: "uchar",
|
|
12
|
+
20: "int8",
|
|
13
|
+
21: "uint8",
|
|
14
|
+
22: "int16",
|
|
15
|
+
23: "uint16",
|
|
16
|
+
24: "int32",
|
|
17
|
+
25: "uint32",
|
|
18
|
+
5: "float32",
|
|
19
|
+
6: "float64",
|
|
20
|
+
}
|
|
21
|
+
OPENMODES = {"r": SDC.READ, "w": SDC.WRITE}
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def open(file_path:str, mode='r', *args, **kwargs):
|
|
25
|
+
return SD(file_path, mode=HDF4.OPENMODES[mode], *args, **kwargs)
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def read(fp:SD, dataset_name:str):
|
|
29
|
+
return fp.select(dataset_name)
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def keys(fp:SD) -> list[str]:
|
|
33
|
+
return list(fp.datasets().keys())
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def infos(fp:SD) -> dict:
|
|
37
|
+
return {name: HDF4.dpinfo(HDF4.read(fp, name)) for name in HDF4.keys(fp)}
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def dpinfo(dp:SDS) -> dict:
|
|
41
|
+
attrs = dp.attributes()
|
|
42
|
+
_info_list = dp.info()
|
|
43
|
+
_info_dict = {
|
|
44
|
+
"dataset_name": _info_list[0],
|
|
45
|
+
"dataset_rank": _info_list[1],
|
|
46
|
+
"dataset_dims": _info_list[2],
|
|
47
|
+
"dataset_type": HDF4.DATATYPES[_info_list[3]]
|
|
48
|
+
}
|
|
49
|
+
_info_dict.update(attrs)
|
|
50
|
+
return _info_dict
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
mtmhdf/_hdf5.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from netCDF4 import Dataset, Variable
|
|
2
|
+
|
|
3
|
+
class HDF5(Dataset):
|
|
4
|
+
@staticmethod
|
|
5
|
+
def open(file_path:str, mode='r', *args, **kwargs):
|
|
6
|
+
return Dataset(file_path, mode=HDF5.OPENMODES[mode], *args, **kwargs)
|
|
7
|
+
|
|
8
|
+
@staticmethod
|
|
9
|
+
def read(fp:Dataset, name:str) -> Variable:
|
|
10
|
+
return HDF5._jump(fp, name)
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def keys(fp:Dataset) -> list[str]:
|
|
14
|
+
return list(HDF5._walk(fp))
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def dpinfo(dp: Variable) -> dict:
|
|
18
|
+
info_dict = dp.__dict__
|
|
19
|
+
info_dict.update({
|
|
20
|
+
"dataset_name": dp.group().path + "/" + dp.name,
|
|
21
|
+
"dataset_dims": dp.shape,
|
|
22
|
+
"dataset_type": dp.datatype.name
|
|
23
|
+
})
|
|
24
|
+
return info_dict
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def infos(fp:Dataset) -> dict:
|
|
28
|
+
return {name: HDF5.dpinfo(HDF5.read(fp, name)) for name in HDF5.keys(fp)}
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def _walk(fp: Dataset, path=""):
|
|
32
|
+
if not len(path) or path[-1] != "/":
|
|
33
|
+
path += "/"
|
|
34
|
+
current_variables = list(fp.variables.keys())
|
|
35
|
+
for variable in current_variables:
|
|
36
|
+
yield path + variable
|
|
37
|
+
current_groups = list(fp.groups.keys())
|
|
38
|
+
for group in current_groups:
|
|
39
|
+
yield from HDF5._walk(fp.groups[group], path + group)
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def _jump(fp: Dataset, path="/"):
|
|
43
|
+
path_list = path.lstrip("/").split("/")
|
|
44
|
+
if not len(path_list):
|
|
45
|
+
return fp
|
|
46
|
+
subnode_fp = fp.__getitem__(path_list[0])
|
|
47
|
+
subnode_path = "/" + "/".join(path_list[1:])
|
|
48
|
+
if len(path_list) == 1:
|
|
49
|
+
return subnode_fp
|
|
50
|
+
else:
|
|
51
|
+
return HDF5._jump(subnode_fp, subnode_path)
|
|
52
|
+
|
mtmhdf/_utils.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from numbers import Number
|
|
3
|
+
|
|
4
|
+
def int2binarystring(num: int, length: int = 0) -> str:
|
|
5
|
+
return bin(num)[2:].zfill(length)
|
|
6
|
+
|
|
7
|
+
def bitoffset(data:np.ndarray | Number, bit_start_pos:int, bit_end_pos:int) -> np.ndarray | Number:
|
|
8
|
+
# 左开右闭区间,即从bit_start_pos开始,到bit_end_pos结束,不包含bit_end_pos
|
|
9
|
+
return (data >> bit_start_pos) % (2 ** (bit_end_pos - bit_start_pos))
|
|
10
|
+
|
|
11
|
+
def scale(data:np.ndarray | Number, scale_factor:Number=1, add_offset:Number=0) -> np.ndarray | Number:
|
|
12
|
+
return data * scale_factor + add_offset
|
|
13
|
+
|
|
14
|
+
def mask(data:np.ndarray | Number, fill_value:Number) -> np.ma.MaskedArray:
|
|
15
|
+
return np.ma.masked_values(data, fill_value, rtol=1e-8, atol=1e-9)
|
mtmhdf/reader.py
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import numpy as np
|
|
3
|
+
import numpy.ma as ma
|
|
4
|
+
|
|
5
|
+
from _hdf4 import HDF4
|
|
6
|
+
from _hdf5 import HDF5
|
|
7
|
+
|
|
8
|
+
from _utils import int2binarystring, bitoffset, scale, mask
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TemplateData:
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
raise NotImplementedError("Subclass must implement this method")
|
|
14
|
+
|
|
15
|
+
def infos(self) -> dict:
|
|
16
|
+
raise NotImplementedError("Subclass must implement this method")
|
|
17
|
+
|
|
18
|
+
def __getitem__(self, *item) -> np.ma.MaskedArray | np.ndarray:
|
|
19
|
+
raise NotImplementedError("Subclass must implement this method")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TemplateReader:
|
|
23
|
+
LinkedDataClass = None
|
|
24
|
+
|
|
25
|
+
def __init__(self, data_file:str, *args, **kwargs):
|
|
26
|
+
raise NotImplementedError("Subclass must implement this method")
|
|
27
|
+
|
|
28
|
+
def read(self, name:str, *args, **kwargs):
|
|
29
|
+
raise NotImplementedError("Subclass must implement this method")
|
|
30
|
+
|
|
31
|
+
def __getitem__(self, name:str):
|
|
32
|
+
return self.rawread(name)
|
|
33
|
+
|
|
34
|
+
def readraw(self, name:str):
|
|
35
|
+
raise NotImplementedError("Subclass must implement this method")
|
|
36
|
+
|
|
37
|
+
def readbit(self, name:str, bit_start_pos: int, bit_end_pos: int, *args, **kwargs) -> np.ma.MaskedArray | np.ndarray:
|
|
38
|
+
# Bit fields within each byte are numbered from the left:
|
|
39
|
+
# 7, 6, 5, 4, 3, 2, 1, 0.
|
|
40
|
+
# The left-most bit (bit 7) is the most significant bit.
|
|
41
|
+
# The right-most bit (bit 0) is the least significant bit.
|
|
42
|
+
# 左开右闭区间,即从bit_start_pos开始,到bit_end_pos结束,不包含bit_end_pos
|
|
43
|
+
dp = self.readraw(name)
|
|
44
|
+
return bitoffset(np.array(dp[:]), bit_start_pos, bit_end_pos)
|
|
45
|
+
|
|
46
|
+
def keys(self) -> list[str]:
|
|
47
|
+
raise NotImplementedError("Subclass must implement this method")
|
|
48
|
+
|
|
49
|
+
def infos(self) -> dict:
|
|
50
|
+
raise NotImplementedError("Subclass must implement this method")
|
|
51
|
+
|
|
52
|
+
# ===================================================================================================
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class HDF4Data(TemplateData):
|
|
56
|
+
def __init__(self, dp:HDF4, mode="manual", isScaleAndOffset: bool = True, isMasked: bool = True, manual_options=None, **kwargs) -> None:
|
|
57
|
+
self.dp = dp
|
|
58
|
+
self.mode = mode
|
|
59
|
+
self.isMasked = isMasked
|
|
60
|
+
self.isScaleAndOffset = isScaleAndOffset
|
|
61
|
+
|
|
62
|
+
default_manual_options = {
|
|
63
|
+
"attr_scale_factor": "scale_factor",
|
|
64
|
+
"attr_add_offset": "add_offset",
|
|
65
|
+
"attr_fill_value": "_FillValue",
|
|
66
|
+
"attr_decimal": 8,
|
|
67
|
+
}
|
|
68
|
+
if manual_options is None:
|
|
69
|
+
self.manual_options = default_manual_options
|
|
70
|
+
else:
|
|
71
|
+
self.manual_options = manual_options.copy()
|
|
72
|
+
self.manual_options.update(default_manual_options)
|
|
73
|
+
|
|
74
|
+
def infos(self):
|
|
75
|
+
return HDF4.dpinfo(self.dp)
|
|
76
|
+
|
|
77
|
+
def manual_transform(self, data: np.ndarray) -> np.ma.MaskedArray|np.ndarray:
|
|
78
|
+
infos: dict = self.infos()
|
|
79
|
+
|
|
80
|
+
attr_scale_factor = self.manual_options["attr_scale_factor"]
|
|
81
|
+
attr_add_offset = self.manual_options["attr_add_offset"]
|
|
82
|
+
attr_fill_value = self.manual_options["attr_fill_value"]
|
|
83
|
+
attr_decimal = self.manual_options["attr_decimal"]
|
|
84
|
+
|
|
85
|
+
scale_factor = round(infos.get(attr_scale_factor, 1), attr_decimal)
|
|
86
|
+
add_offset = round(infos.get(attr_add_offset, 0), attr_decimal)
|
|
87
|
+
fill_value = infos.get(attr_fill_value)
|
|
88
|
+
|
|
89
|
+
if self.isMasked:
|
|
90
|
+
data = mask(data, fill_value)
|
|
91
|
+
if self.isScaleAndOffset:
|
|
92
|
+
data = scale(data, scale_factor, add_offset)
|
|
93
|
+
return data
|
|
94
|
+
|
|
95
|
+
def __getitem__(self, *item) -> np.ma.MaskedArray | np.ndarray:
|
|
96
|
+
data = self.dp.__getitem__(*item)
|
|
97
|
+
if self.mode == "native":
|
|
98
|
+
return data
|
|
99
|
+
elif self.mode == "manual":
|
|
100
|
+
return self.manual_transform(np.array(data))
|
|
101
|
+
else:
|
|
102
|
+
raise ValueError(f"Invalid mode: {self.mode}")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class HDF4Reader(TemplateReader):
|
|
106
|
+
LinkedDataClass = HDF4Data
|
|
107
|
+
|
|
108
|
+
def __init__(self, data_file:str, *args, **kwargs):
|
|
109
|
+
self.fp = HDF4.open(data_file, *args, **kwargs)
|
|
110
|
+
|
|
111
|
+
def readraw(self, name:str):
|
|
112
|
+
return HDF4.read(self.fp, name)
|
|
113
|
+
|
|
114
|
+
def read(self, name:str, isScaleAndOffset: bool = True, isMasked: bool = True, **kwargs):
|
|
115
|
+
dp = HDF4.read(self.fp, name)
|
|
116
|
+
DataClass = HDF4Reader.LinkedDataClass
|
|
117
|
+
return DataClass(dp, mode="manual", isScaleAndOffset=isScaleAndOffset, isMasked=isMasked, **kwargs)
|
|
118
|
+
|
|
119
|
+
def keys(self) -> list[str]:
|
|
120
|
+
return HDF4.keys(self.fp)
|
|
121
|
+
|
|
122
|
+
def infos(self) -> dict:
|
|
123
|
+
return HDF4.infos(self.fp)
|
|
124
|
+
|
|
125
|
+
# ===================================================================================================
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class HDF5Data(TemplateData):
|
|
129
|
+
def __init__(self, dp, mode="manual", isScaleAndOffset: bool = False, isMasked: bool = True, manual_options=None, **kwargs) -> None:
|
|
130
|
+
self.dp = dp
|
|
131
|
+
self.mode = mode
|
|
132
|
+
self.isScaleAndOffset = isScaleAndOffset
|
|
133
|
+
self.isMasked = isMasked
|
|
134
|
+
|
|
135
|
+
default_manual_options = {
|
|
136
|
+
"attr_scale_factor": "scale_factor",
|
|
137
|
+
"attr_add_offset": "add_offset",
|
|
138
|
+
"attr_fill_value": "_FillValue",
|
|
139
|
+
"attr_decimal": 8,
|
|
140
|
+
}
|
|
141
|
+
if manual_options is None:
|
|
142
|
+
self.manual_options = default_manual_options
|
|
143
|
+
else:
|
|
144
|
+
self.manual_options = manual_options.copy()
|
|
145
|
+
self.manual_options.update(default_manual_options)
|
|
146
|
+
|
|
147
|
+
if mode == "manual":
|
|
148
|
+
self.dp.set_auto_scale(False)
|
|
149
|
+
self.dp.set_auto_mask(False)
|
|
150
|
+
elif mode == "native":
|
|
151
|
+
self.dp.set_auto_scale(isScaleAndOffset)
|
|
152
|
+
self.dp.set_auto_mask(isMasked)
|
|
153
|
+
else:
|
|
154
|
+
raise ValueError(f"Invalid mode: {self.mode}")
|
|
155
|
+
|
|
156
|
+
def infos(self):
|
|
157
|
+
return HDF5.dpinfo(self.dp)
|
|
158
|
+
|
|
159
|
+
def manual_transform(self, data: np.ndarray) -> np.ma.MaskedArray|np.ndarray:
|
|
160
|
+
infos: dict = self.infos()
|
|
161
|
+
|
|
162
|
+
attr_scale_factor = self.manual_options["attr_scale_factor"]
|
|
163
|
+
attr_add_offset = self.manual_options["attr_add_offset"]
|
|
164
|
+
attr_fill_value = self.manual_options["attr_fill_value"]
|
|
165
|
+
attr_decimal = self.manual_options["attr_decimal"]
|
|
166
|
+
scale_factor = round(infos.get(attr_scale_factor, 1), attr_decimal)
|
|
167
|
+
add_offset = round(infos.get(attr_add_offset, 0), attr_decimal)
|
|
168
|
+
fill_value = infos.get(attr_fill_value)
|
|
169
|
+
|
|
170
|
+
if self.isMasked:
|
|
171
|
+
data = mask(data, fill_value)
|
|
172
|
+
if self.isScaleAndOffset:
|
|
173
|
+
data = scale(data, scale_factor, add_offset)
|
|
174
|
+
return data
|
|
175
|
+
|
|
176
|
+
def __getitem__(self, *item) -> np.ma.MaskedArray | np.ndarray:
|
|
177
|
+
data = self.dp.__getitem__(*item)
|
|
178
|
+
if self.mode == "native":
|
|
179
|
+
return data
|
|
180
|
+
elif self.mode == "manual":
|
|
181
|
+
return self.manual_transform(np.array(data))
|
|
182
|
+
else:
|
|
183
|
+
raise ValueError(f"Invalid mode: {self.mode}")
|
|
184
|
+
|
|
185
|
+
class HDF5Reader(TemplateReader):
|
|
186
|
+
LinkedDataClass = HDF5Data
|
|
187
|
+
|
|
188
|
+
def __init__(self, data_file:str, *args, **kwargs):
|
|
189
|
+
self.fp = HDF5.open(data_file, *args, **kwargs)
|
|
190
|
+
|
|
191
|
+
def readraw(self, name:str):
|
|
192
|
+
return HDF5.read(self.fp, name)
|
|
193
|
+
|
|
194
|
+
def read(self, name:str, isScaleAndOffset: bool = True, isMasked: bool = True, mode="native", **kwargs):
|
|
195
|
+
dp = HDF5.read(self.fp, name)
|
|
196
|
+
DataClass = HDF5Reader.LinkedDataClass
|
|
197
|
+
return DataClass(dp, mode=mode, isScaleAndOffset=isScaleAndOffset, isMasked=isMasked, **kwargs)
|
|
198
|
+
|
|
199
|
+
def keys(self) -> list[str]:
|
|
200
|
+
return HDF5.keys(self.fp)
|
|
201
|
+
|
|
202
|
+
def infos(self) -> dict:
|
|
203
|
+
return HDF5.infos(self.fp)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def Reader(data_file: str, *args, **kwargs) -> TemplateReader:
|
|
207
|
+
_, ext = os.path.splitext(data_file)
|
|
208
|
+
ext = ext.lower()
|
|
209
|
+
if ext in ['.nc', '.h5', '.he5', '.hdf5']:
|
|
210
|
+
return HDF5Reader(data_file, *args, **kwargs)
|
|
211
|
+
elif ext in ['.hdf', '.hdf4']:
|
|
212
|
+
return HDF4Reader(data_file, *args, **kwargs)
|
|
213
|
+
else:
|
|
214
|
+
raise ValueError(f"Unsupported file extension: {ext}")
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mtmhdf
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: imutum's packages for HDF
|
|
5
|
+
License: MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2023 imutum
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
|
|
27
|
+
Classifier: Development Status :: 3 - Alpha
|
|
28
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
29
|
+
Classifier: Intended Audience :: Developers
|
|
30
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
31
|
+
Classifier: Operating System :: MacOS
|
|
32
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
33
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
34
|
+
Requires-Python: >=3.11
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
License-File: LICENSE
|
|
37
|
+
Requires-Dist: pyhdf>=0.10.5
|
|
38
|
+
Requires-Dist: netcdf4>=1.6.5
|
|
39
|
+
Requires-Dist: numpy>=1.26.0
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# imutum_python_hdf
|
|
43
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
mtmhdf/__init__.py,sha256=P4ZUG3Klkmyx1S3RbAxzL8VzWm0P7bQoRJBgbZ3M2eo,26
|
|
2
|
+
mtmhdf/_hdf4.py,sha256=RonrjU2D0v4eG0TcluY6Xb0nKYKY-vcxu6BWEB3tZx4,1235
|
|
3
|
+
mtmhdf/_hdf5.py,sha256=tux71Zey-JyAcvE1r6rTl165xlPVDxmUiWbS_brMQrk,1654
|
|
4
|
+
mtmhdf/_utils.py,sha256=8YeVA2NPu8MoW9pRAw9VTbSwdwvjktgt2M1IQc7VcdQ,711
|
|
5
|
+
mtmhdf/reader.py,sha256=Hi1uN7nh7lRgwuJUr5TesWL1YfDLCGsYDrmEt9PBm7s,7856
|
|
6
|
+
mtmhdf-0.0.2.dist-info/licenses/LICENSE,sha256=-lZJZIaH__2OkAxU2yFoe0cHHe48x8s2LIxCC3KFUqA,1063
|
|
7
|
+
mtmhdf-0.0.2.dist-info/METADATA,sha256=V-9N4BYwexAHmmgDFw1QNbmm-DTAfKgusAlRmVlZrzk,1873
|
|
8
|
+
mtmhdf-0.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
9
|
+
mtmhdf-0.0.2.dist-info/top_level.txt,sha256=eBIgfbDXWQslU5lXIbDr0x77IXgUqXKTu6OGzdipLMs,7
|
|
10
|
+
mtmhdf-0.0.2.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 imutum
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mtmhdf
|