ModDotPlot 1.0.0__cp38-abi3-win_amd64.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.
moddotplot/__init__.py ADDED
@@ -0,0 +1,29 @@
1
+ """""" # start delvewheel patch
2
+ def _delvewheel_patch_1_13_0():
3
+ import ctypes
4
+ import os
5
+ import platform
6
+ import sys
7
+ libs_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, 'moddotplot.libs'))
8
+ is_conda_cpython = platform.python_implementation() == 'CPython' and (hasattr(ctypes.pythonapi, 'Anaconda_GetVersion') or 'packaged by conda-forge' in sys.version)
9
+ if sys.version_info[:2] >= (3, 8) and not is_conda_cpython or sys.version_info[:2] >= (3, 10):
10
+ if os.path.isdir(libs_dir):
11
+ os.add_dll_directory(libs_dir)
12
+ else:
13
+ load_order_filepath = os.path.join(libs_dir, '.load-order-moddotplot-1.0.0')
14
+ if os.path.isfile(load_order_filepath):
15
+ import ctypes.wintypes
16
+ with open(os.path.join(libs_dir, '.load-order-moddotplot-1.0.0')) as file:
17
+ load_order = file.read().split()
18
+ kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
19
+ kernel32.LoadLibraryExW.restype = ctypes.wintypes.HMODULE
20
+ kernel32.LoadLibraryExW.argtypes = ctypes.wintypes.LPCWSTR, ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD
21
+ for lib in load_order:
22
+ lib_path = os.path.join(os.path.join(libs_dir, lib))
23
+ if os.path.isfile(lib_path) and not kernel32.LoadLibraryExW(lib_path, None, 8):
24
+ raise OSError('Error loading {}; {}'.format(lib, ctypes.FormatError(ctypes.get_last_error())))
25
+
26
+
27
+ _delvewheel_patch_1_13_0()
28
+ del _delvewheel_patch_1_13_0
29
+ # end delvewheel patch
moddotplot/__main__.py ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env python3
2
+ from contextlib import redirect_stderr, redirect_stdout
3
+ import os
4
+ import sys
5
+
6
+
7
+ def _run(arguments):
8
+ from moddotplot.moddotplot import main as run_moddotplot
9
+
10
+ return run_moddotplot(arguments)
11
+
12
+
13
+ def main(arguments=None):
14
+ """Load the CLI under quiet redirection to cover import-time output."""
15
+
16
+ raw_arguments = list(sys.argv[1:] if arguments is None else arguments)
17
+ if "--quiet" not in raw_arguments:
18
+ return _run(raw_arguments)
19
+
20
+ with open(os.devnull, "w", encoding="utf-8") as sink:
21
+ with redirect_stdout(sink), redirect_stderr(sink):
22
+ return _run(raw_arguments)
23
+
24
+
25
+ if __name__ == "__main__":
26
+ sys.exit(main())
moddotplot/_nthash.pyd ADDED
Binary file
@@ -0,0 +1,113 @@
1
+ """Shared BED annotation parsing and interval-selection helpers."""
2
+
3
+ import numpy as np
4
+ import pandas as pd
5
+
6
+ DEFAULT_ANNOTATION_COLOR = "#4C72B0"
7
+ BED_COLUMNS = [
8
+ "chrom",
9
+ "start",
10
+ "end",
11
+ "name",
12
+ "score",
13
+ "strand",
14
+ "thickStart",
15
+ "thickEnd",
16
+ "itemRgb",
17
+ ]
18
+
19
+
20
+ def read_annotation_bed(filepath):
21
+ """Read the BED3-BED9 subset used by ModDotPlot annotations."""
22
+
23
+ try:
24
+ dataframe = pd.read_csv(filepath, sep="\t", comment="#", header=None, dtype=str)
25
+ except pd.errors.EmptyDataError:
26
+ return pd.DataFrame(columns=BED_COLUMNS[:3])
27
+
28
+ if not 3 <= dataframe.shape[1] <= len(BED_COLUMNS):
29
+ raise ValueError(
30
+ "Invalid BED file: expected between 3 and 9 tab-separated columns."
31
+ )
32
+
33
+ dataframe.columns = BED_COLUMNS[: dataframe.shape[1]]
34
+ dataframe["chrom"] = dataframe["chrom"].astype(str)
35
+
36
+ for column in ("start", "end"):
37
+ try:
38
+ values = pd.to_numeric(dataframe[column], errors="raise")
39
+ except (TypeError, ValueError) as error:
40
+ raise ValueError(
41
+ f"Invalid BED file: '{column}' must contain only integers."
42
+ ) from error
43
+ if values.isna().any() or not np.all(np.isfinite(values)):
44
+ raise ValueError(
45
+ f"Invalid BED file: '{column}' must contain only finite integers."
46
+ )
47
+ if np.any(values % 1 != 0):
48
+ raise ValueError(
49
+ f"Invalid BED file: '{column}' must contain only integers."
50
+ )
51
+ dataframe[column] = values.astype(np.int64)
52
+
53
+ if (dataframe["start"] < 0).any():
54
+ raise ValueError("Invalid BED file: 'start' must be non-negative.")
55
+ if (dataframe["end"] <= dataframe["start"]).any():
56
+ raise ValueError(
57
+ "Invalid BED file: 'end' must be greater than 'start' for every interval."
58
+ )
59
+
60
+ return dataframe
61
+
62
+
63
+ def read_annotation_beds(filepaths):
64
+ """Read and combine one or more annotation BED files."""
65
+
66
+ frames = [read_annotation_bed(filepath) for filepath in filepaths]
67
+ if not frames:
68
+ return pd.DataFrame(columns=BED_COLUMNS[:3])
69
+ return pd.concat(frames, ignore_index=True, sort=False)
70
+
71
+
72
+ def annotation_color(value, fallback=DEFAULT_ANNOTATION_COLOR):
73
+ """Return an RGB tuple for a BED ``itemRgb`` value."""
74
+
75
+ if value is None or pd.isna(value):
76
+ return fallback
77
+
78
+ fields = [field.strip() for field in str(value).split(",")]
79
+ if len(fields) != 3:
80
+ return fallback
81
+
82
+ try:
83
+ channels = tuple(int(field) for field in fields)
84
+ except ValueError:
85
+ return fallback
86
+ if any(channel < 0 or channel > 255 for channel in channels):
87
+ return fallback
88
+ return tuple(channel / 255 for channel in channels)
89
+
90
+
91
+ def visible_annotation_intervals(
92
+ bed_df, chrom, region_start, region_end, fallback=DEFAULT_ANNOTATION_COLOR
93
+ ):
94
+ """Select and clip BED intervals to a plotted genomic region."""
95
+
96
+ if region_end <= region_start:
97
+ raise ValueError("Annotation region end must be greater than its start.")
98
+ if bed_df.empty:
99
+ return []
100
+
101
+ intervals = []
102
+ matching = bed_df[bed_df["chrom"] == str(chrom)]
103
+ has_item_rgb = "itemRgb" in matching.columns
104
+ for row in matching.itertuples(index=False):
105
+ interval_start = int(row.start)
106
+ interval_end = int(row.end)
107
+ clipped_start = max(interval_start, region_start)
108
+ clipped_end = min(interval_end, region_end)
109
+ if clipped_end <= clipped_start:
110
+ continue
111
+ rgb = getattr(row, "itemRgb", None) if has_item_rgb else None
112
+ intervals.append((clipped_start, clipped_end, annotation_color(rgb, fallback)))
113
+ return intervals