epyraw 0.1.0__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.
epyraw/__init__.py ADDED
@@ -0,0 +1,113 @@
1
+ """Epyraw - Python wrapper for Thermo Fisher RawFileReader library.
2
+
3
+ This package provides a Pythonic interface to read Thermo Scientific .raw
4
+ mass spectrometry files using the official RawFileReader .NET library.
5
+
6
+ Example:
7
+ >>> from epyraw import RawFile, Device
8
+ >>> with RawFile("data.raw") as rf:
9
+ ... rf.select_instrument(Device.MS, 1)
10
+ ... print(f"Scans: {rf.run_header.spectra_count}")
11
+ ... spectrum = rf.get_spectrum(100)
12
+ ... print(f"Peaks: {spectrum.num_peaks}")
13
+ """
14
+
15
+ from .download import check_runtimes_present, download_runtimes, get_default_runtime_path
16
+ from .enums import (
17
+ BarcodeStatusType,
18
+ DetectorType,
19
+ Device,
20
+ IonizationMode,
21
+ MassAnalyzerType,
22
+ MSOrderType,
23
+ PolarityType,
24
+ SampleType,
25
+ ScanTypeEnum,
26
+ SpectrumPacketType,
27
+ ToleranceUnits,
28
+ TraceType,
29
+ TrayShape,
30
+ )
31
+ from .exceptions import (
32
+ AssemblyLoadError,
33
+ InitializationError,
34
+ InstrumentNotSelectedError,
35
+ InvalidScanNumberError,
36
+ RawFileCorruptedError,
37
+ RawFileNotFoundError,
38
+ RawFileOpenError,
39
+ ThermoRawFileError,
40
+ )
41
+ from .models import (
42
+ AutoSamplerInformation,
43
+ Chromatogram,
44
+ FileHeader,
45
+ InclusionListItem,
46
+ InstrumentData,
47
+ MassOptions,
48
+ Reaction,
49
+ RunHeader,
50
+ SampleInformation,
51
+ ScanDependentDetail,
52
+ ScanDependents,
53
+ ScanEvent,
54
+ ScanFilter,
55
+ ScanHeader,
56
+ ScanStats,
57
+ Spectrum,
58
+ StatusLogField,
59
+ )
60
+ from .raw_file import RawFile, set_runtime
61
+
62
+ __version__ = "0.1.0"
63
+
64
+ __all__ = [
65
+ # Main class
66
+ "RawFile",
67
+ "set_runtime",
68
+ # Download functions
69
+ "download_runtimes",
70
+ "check_runtimes_present",
71
+ "get_default_runtime_path",
72
+ # Enums
73
+ "Device",
74
+ "DetectorType",
75
+ "IonizationMode",
76
+ "MSOrderType",
77
+ "MassAnalyzerType",
78
+ "PolarityType",
79
+ "SampleType",
80
+ "ToleranceUnits",
81
+ "TraceType",
82
+ "SpectrumPacketType",
83
+ "ScanTypeEnum",
84
+ "TrayShape",
85
+ "BarcodeStatusType",
86
+ # Data models
87
+ "Chromatogram",
88
+ "FileHeader",
89
+ "InclusionListItem",
90
+ "InstrumentData",
91
+ "RunHeader",
92
+ "SampleInformation",
93
+ "ScanFilter",
94
+ "Spectrum",
95
+ "StatusLogField",
96
+ "ScanHeader",
97
+ "ScanEvent",
98
+ "Reaction",
99
+ "MassOptions",
100
+ "ScanStats",
101
+ "ScanDependents",
102
+ "ScanDependentDetail",
103
+ "AutoSamplerInformation",
104
+ # Exceptions
105
+ "AssemblyLoadError",
106
+ "InitializationError",
107
+ "InstrumentNotSelectedError",
108
+ "InvalidScanNumberError",
109
+ "RawFileCorruptedError",
110
+ "RawFileNotFoundError",
111
+ "RawFileOpenError",
112
+ "ThermoRawFileError",
113
+ ]
epyraw/cli.py ADDED
@@ -0,0 +1,71 @@
1
+ """CLI for epyraw package."""
2
+
3
+ import argparse
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ from .download import check_runtimes_present, download_runtimes, get_default_runtime_path
8
+
9
+
10
+ def main() -> None:
11
+ """Main CLI entry point."""
12
+ parser = argparse.ArgumentParser(
13
+ prog="epyraw",
14
+ description="Thermo RawFileReader Python wrapper - manage runtime DLLs",
15
+ )
16
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
17
+
18
+ # Download command
19
+ download_parser = subparsers.add_parser(
20
+ "download", help="Download Thermo RawFileReader runtime DLLs"
21
+ )
22
+ download_parser.add_argument(
23
+ "--path",
24
+ "-p",
25
+ type=str,
26
+ help="Custom path for DLLs (default: ~/.epyraw/runtimes/net8old)",
27
+ )
28
+ download_parser.add_argument(
29
+ "--force",
30
+ "-f",
31
+ action="store_true",
32
+ help="Re-download even if files exist",
33
+ )
34
+
35
+ # Check command
36
+ check_parser = subparsers.add_parser("check", help="Check if runtime DLLs are installed")
37
+ check_parser.add_argument(
38
+ "--path",
39
+ "-p",
40
+ type=str,
41
+ help="Custom path to check (default: ~/.epyraw/runtimes/net8old)",
42
+ )
43
+
44
+ args = parser.parse_args()
45
+
46
+ if args.command == "download":
47
+ target_path = Path(args.path) if args.path else None
48
+ print("Downloading Thermo RawFileReader runtime DLLs...")
49
+ try:
50
+ path = download_runtimes(target_path, force=args.force)
51
+ print(f"Successfully downloaded to: {path}")
52
+ except Exception as e:
53
+ print(f"Error: {e}", file=sys.stderr)
54
+ sys.exit(1)
55
+
56
+ elif args.command == "check":
57
+ path = Path(args.path) if args.path else get_default_runtime_path()
58
+ if check_runtimes_present(path):
59
+ print(f"Runtime DLLs found at: {path}")
60
+ sys.exit(0)
61
+ else:
62
+ print(f"Runtime DLLs NOT found at: {path}")
63
+ print("Run 'epyraw download' to install them.")
64
+ sys.exit(1)
65
+ else:
66
+ parser.print_help()
67
+ sys.exit(0)
68
+
69
+
70
+ if __name__ == "__main__":
71
+ main()
epyraw/download.py ADDED
@@ -0,0 +1,158 @@
1
+ """Download Thermo RawFileReader runtime DLLs.
2
+
3
+ This module provides functionality to download the required .NET assemblies
4
+ from the Thermo Fisher RawFileReader GitHub repository.
5
+ """
6
+
7
+ from pathlib import Path
8
+
9
+ try:
10
+ import requests
11
+ except ImportError:
12
+ requests = None # type: ignore
13
+
14
+
15
+ # GitHub raw content URLs for the DLLs
16
+ RAW_FILE_READER_URLS = {
17
+ "base_url": "https://raw.githubusercontent.com/thermofisherlsms/RawFileReader/main/Libs/NetCore/Net8Old/Assemblies/",
18
+ "files": [
19
+ "OpenMcdf.dll",
20
+ "OpenMcdf.Extensions.dll",
21
+ "ThermoFisher.CommonCore.BackgroundSubtraction.dll",
22
+ "ThermoFisher.CommonCore.BackgroundSubtraction.xml",
23
+ "ThermoFisher.CommonCore.Data.dll",
24
+ "ThermoFisher.CommonCore.Data.xml",
25
+ "ThermoFisher.CommonCore.MassPrecisionEstimator.dll",
26
+ "ThermoFisher.CommonCore.MassPrecisionEstimator.xml",
27
+ "ThermoFisher.CommonCore.RawFileReader.dll",
28
+ "ThermoFisher.CommonCore.RawFileReader.xml",
29
+ ],
30
+ }
31
+
32
+
33
+ def get_default_runtime_path() -> Path:
34
+ """Get the default path for runtime DLLs.
35
+
36
+ Returns:
37
+ Path to ~/.epyraw/runtimes/net8old/
38
+ """
39
+ return Path.home() / ".epyraw" / "runtimes" / "net8old"
40
+
41
+
42
+ def download_runtimes(
43
+ target_path: Path | None = None, force: bool = False, progress: bool = True
44
+ ) -> Path:
45
+ """Download the Thermo RawFileReader runtime DLLs.
46
+
47
+ Args:
48
+ target_path: Optional custom path for DLLs. Defaults to ~/.epyraw/runtimes/net8old/
49
+ force: If True, re-download even if files exist
50
+ progress: If True, print progress messages
51
+
52
+ Returns:
53
+ Path to the directory containing the downloaded DLLs
54
+
55
+ Raises:
56
+ RuntimeError: If download fails
57
+ ImportError: If requests library is not installed
58
+ """
59
+ if requests is None:
60
+ raise ImportError(
61
+ "requests library is required for downloading runtimes. "
62
+ "Install it with: pip install requests"
63
+ )
64
+
65
+ if target_path is None:
66
+ target_path = get_default_runtime_path()
67
+
68
+ # Create target directory
69
+ target_path.mkdir(parents=True, exist_ok=True)
70
+
71
+ base_url = RAW_FILE_READER_URLS["base_url"]
72
+ files = RAW_FILE_READER_URLS["files"]
73
+
74
+ downloaded = []
75
+ failed = []
76
+
77
+ for filename in files:
78
+ file_path = target_path / filename
79
+ if file_path.exists() and not force:
80
+ if progress:
81
+ print(f" Skipping {filename} (already exists)")
82
+ downloaded.append(file_path)
83
+ continue
84
+
85
+ url = base_url + filename
86
+ try:
87
+ if progress:
88
+ print(f" Downloading {filename}...")
89
+ response = requests.get(url, timeout=30)
90
+ response.raise_for_status()
91
+
92
+ with open(file_path, "wb") as f:
93
+ f.write(response.content)
94
+ downloaded.append(file_path)
95
+ except Exception as e:
96
+ # Clean up partial download
97
+ if file_path.exists():
98
+ file_path.unlink()
99
+ failed.append((filename, str(e)))
100
+
101
+ if failed:
102
+ error_msg = "\n".join(f" {name}: {err}" for name, err in failed)
103
+ raise RuntimeError(f"Failed to download some files:\n{error_msg}")
104
+
105
+ if progress:
106
+ print(f"Successfully downloaded {len(downloaded)} files to: {target_path}")
107
+
108
+ return target_path
109
+
110
+
111
+ def check_runtimes_present(path: Path | None = None) -> bool:
112
+ """Check if runtime DLLs are present.
113
+
114
+ Args:
115
+ path: Optional custom path. Defaults to ~/.epyraw/runtimes/net8old/
116
+
117
+ Returns:
118
+ True if all required DLLs are present
119
+ """
120
+ if path is None:
121
+ path = get_default_runtime_path()
122
+
123
+ required_files = [
124
+ "ThermoFisher.CommonCore.RawFileReader.dll",
125
+ "ThermoFisher.CommonCore.Data.dll",
126
+ "ThermoFisher.CommonCore.BackgroundSubtraction.dll",
127
+ "ThermoFisher.CommonCore.MassPrecisionEstimator.dll",
128
+ "OpenMcdf.dll",
129
+ ]
130
+
131
+ return all((path / f).exists() for f in required_files)
132
+
133
+
134
+ def ensure_runtimes_available(runtime_path: Path | None = None) -> Path:
135
+ """Ensure runtime DLLs are available, downloading if necessary.
136
+
137
+ Args:
138
+ runtime_path: Optional custom path. Checks default location if not provided.
139
+
140
+ Returns:
141
+ Path to the runtime directory
142
+
143
+ Raises:
144
+ RuntimeError: If runtimes cannot be downloaded
145
+ """
146
+ # Check provided path first
147
+ if runtime_path is not None and Path(runtime_path).exists():
148
+ return Path(runtime_path)
149
+
150
+ # Check default user path
151
+ default_path = get_default_runtime_path()
152
+ if default_path.exists() and check_runtimes_present(default_path):
153
+ return default_path
154
+
155
+ # Try to download
156
+ print("Thermo RawFileReader runtime DLLs not found.")
157
+ print("Downloading from GitHub...")
158
+ return download_runtimes()
epyraw/enums.py ADDED
@@ -0,0 +1,208 @@
1
+ """Enum types for Thermo RawFile Reader.
2
+
3
+ These enums mirror the .NET enum types from the RawFileReader library,
4
+ providing a Pythonic interface to filter and select data.
5
+ """
6
+
7
+ from enum import IntEnum
8
+
9
+
10
+ class Device(IntEnum):
11
+ """Instrument device types.
12
+
13
+ Values match the Thermo RawFileReader .NET Device enum:
14
+ https://github.com/thermofisherlsms/RawFileReader
15
+ """
16
+
17
+ MS = 0
18
+ MSAnalog = 1
19
+ Analog = 2
20
+ Other = 5
21
+ Pda = 4
22
+ UV = 3
23
+
24
+
25
+ class MSOrderType(IntEnum):
26
+ """Mass spectrometry order."""
27
+
28
+ Unknown = 0
29
+ Ms = 1
30
+ Ms2 = 2
31
+ Ms3 = 3
32
+ MsN = 4
33
+
34
+
35
+ class IonizationMode(IntEnum):
36
+ """Ionization mode."""
37
+
38
+ Unknown = 0
39
+ Positive = 1
40
+ Negative = 2
41
+
42
+
43
+ class PolarityType(IntEnum):
44
+ """Polarity type."""
45
+
46
+ Unknown = 0
47
+ Positive = 1
48
+ Negative = 2
49
+
50
+
51
+ class MassAnalyzerType(IntEnum):
52
+ """Mass analyzer types."""
53
+
54
+ Unknown = 0
55
+ Quadrupole = 1
56
+ IonTrap = 2
57
+ TOF = 3
58
+ Orbitrap = 4
59
+ FTICR = 5
60
+ Sector = 6
61
+
62
+
63
+ class DetectorType(IntEnum):
64
+ """Detector types."""
65
+
66
+ Unknown = 0
67
+ ElectronMultiplier = 1
68
+ FaradayCup = 2
69
+ PhotodiodeArray = 3
70
+ CCD = 4
71
+
72
+
73
+ class TraceType(IntEnum):
74
+ """Chromatogram trace types."""
75
+
76
+ BasePeak = 0
77
+ TIC = 1
78
+ A2DChannel1 = 2
79
+ A2DChannel2 = 3
80
+ A2DChannel3 = 4
81
+ A2DChannel4 = 5
82
+ Analog1 = 6
83
+ Analog2 = 7
84
+ Analog3 = 8
85
+ Analog4 = 9
86
+ UV1 = 10
87
+ UV2 = 11
88
+ UV3 = 12
89
+ PDA1 = 13
90
+ PDA2 = 14
91
+ PDA3 = 15
92
+
93
+
94
+ class ToleranceUnits(IntEnum):
95
+ """Mass tolerance units."""
96
+
97
+ Daltons = 0
98
+ ppm = 1
99
+
100
+
101
+ class SampleType(IntEnum):
102
+ """Sample types."""
103
+
104
+ Unknown = 0
105
+ Blank = 1
106
+ Standard = 2
107
+ QualityControl = 3
108
+ Sample = 4
109
+ CalibrationStandard = 5
110
+ SystemSuitability = 6
111
+
112
+
113
+ class GenericDataTypes(IntEnum):
114
+ """Generic data types for status log and trailer extra fields."""
115
+
116
+ NULL = 0
117
+ Double = 1
118
+ String = 2
119
+ DateTime = 3
120
+ Int32 = 4
121
+ Int64 = 5
122
+
123
+
124
+ class SpectrumPacketType(IntEnum):
125
+ """Type of spectrum data packet.
126
+
127
+ Values match the Thermo RawFileReader .NET SpectrumPacketType enum.
128
+ """
129
+
130
+ FtCentroid = 0
131
+ """Fourier transform centroid data."""
132
+
133
+ FtProfile = 1
134
+ """Fourier transform profile data."""
135
+
136
+ QuadrupoleCentroid = 2
137
+ """Quadrupole centroid data."""
138
+
139
+ QuadrupoleProfile = 3
140
+ """Quadrupole profile data."""
141
+
142
+
143
+ class ScanTypeEnum(IntEnum):
144
+ """Type of scan performed.
145
+
146
+ Values match common Thermo instrument scan types.
147
+ """
148
+
149
+ Full = 0
150
+ """Full scan."""
151
+
152
+ SIM = 1
153
+ """Selected ion monitoring (SIM) scan."""
154
+
155
+ ProductIon = 2
156
+ """Product ion scan."""
157
+
158
+ DataDependent = 3
159
+ """Data-dependent scan."""
160
+
161
+ TopN = 4
162
+ """Top N scan."""
163
+
164
+
165
+ class TrayShape(IntEnum):
166
+ """Autosampler tray shape.
167
+
168
+ Values match the Thermo RawFileReader .NET TrayShape enum.
169
+ """
170
+
171
+ Rectangular = 0
172
+ """Rectangular tray layout."""
173
+
174
+ Circular = 1
175
+ """Circular tray layout."""
176
+
177
+ StaggeredOdd = 2
178
+ """Staggered layout for odd rows."""
179
+
180
+ StaggeredEven = 3
181
+ """Staggered layout for even rows."""
182
+
183
+ Unknown = 4
184
+ """Unknown tray shape."""
185
+
186
+ Invalid = 5
187
+ """Invalid tray shape."""
188
+
189
+
190
+ class BarcodeStatusType(IntEnum):
191
+ """Barcode read status.
192
+
193
+ Values match the Thermo RawFileReader .NET BarcodeStatusType enum.
194
+ """
195
+
196
+ NotRead = 0
197
+ """Barcode was not read."""
198
+
199
+ Read = 1
200
+ """Barcode was successfully read."""
201
+
202
+ ReadFailed = 2
203
+ """Barcode read failed."""
204
+
205
+
206
+ # Type aliases for clarity
207
+ TraceTypeName = str
208
+ """Type alias for trace type names."""
epyraw/exceptions.py ADDED
@@ -0,0 +1,49 @@
1
+ """Custom exceptions for Thermo RawFile Reader."""
2
+
3
+
4
+ class ThermoRawFileError(Exception):
5
+ """Base exception for Thermo RawFile Reader errors."""
6
+
7
+ pass
8
+
9
+
10
+ class RawFileNotFoundError(ThermoRawFileError):
11
+ """Raised when the RAW file cannot be found."""
12
+
13
+ pass
14
+
15
+
16
+ class RawFileOpenError(ThermoRawFileError):
17
+ """Raised when the RAW file cannot be opened."""
18
+
19
+ pass
20
+
21
+
22
+ class RawFileCorruptedError(ThermoRawFileError):
23
+ """Raised when the RAW file is corrupted or has read errors."""
24
+
25
+ pass
26
+
27
+
28
+ class InvalidScanNumberError(ThermoRawFileError):
29
+ """Raised when an invalid scan number is requested."""
30
+
31
+ pass
32
+
33
+
34
+ class InstrumentNotSelectedError(ThermoRawFileError):
35
+ """Raised when an operation requires instrument selection."""
36
+
37
+ pass
38
+
39
+
40
+ class AssemblyLoadError(ThermoRawFileError):
41
+ """Raised when a .NET assembly fails to load."""
42
+
43
+ pass
44
+
45
+
46
+ class InitializationError(ThermoRawFileError):
47
+ """Raised when the .NET runtime fails to initialize."""
48
+
49
+ pass