loom-client 0.0.1__tar.gz

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.
@@ -0,0 +1,227 @@
1
+ Metadata-Version: 2.4
2
+ Name: loom-client
3
+ Version: 0.0.1
4
+ Summary: Python WebSocket client for the Loom Genome Browser
5
+ Requires-Python: >=3.11
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: pydantic>=2.0
8
+ Requires-Dist: websockets>=12.0
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest>=7.0; extra == "dev"
11
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
12
+
13
+ # loom-client
14
+
15
+ Python WebSocket client for the [Loom Genome Browser](https://github.com/nicholasrq/loom). Provides a fully async, typed interface for programmatically controlling a running Loom instance -- navigating loci, managing tracks and regions of interest, exporting views, and subscribing to browser events.
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ pip install loom-client
21
+ ```
22
+
23
+ Or install from source:
24
+
25
+ ```bash
26
+ git clone https://github.com/riyavsinha/loom-client.git
27
+ cd loom-client
28
+ pip install -e .
29
+ ```
30
+
31
+ Requires Python >= 3.11.
32
+
33
+ ## Quick start
34
+
35
+ ```python
36
+ import asyncio
37
+ from loom_client import LoomClient
38
+
39
+ async def main():
40
+ client = LoomClient("ws://localhost:8080")
41
+ await client.connect()
42
+
43
+ # Get current browser state
44
+ state = await client.get_browser_state()
45
+ print(state.locus_string) # e.g. "chr1:1,000-2,000"
46
+ print(state.zoom_level) # e.g. ZoomLevel.GENE
47
+
48
+ # Navigate to a locus
49
+ await client.navigate(locus="chr17:7565097-7590856") # TP53
50
+
51
+ # Zoom out 2x
52
+ await client.navigate(zoom="out", factor=2.0)
53
+
54
+ await client.close()
55
+
56
+ asyncio.run(main())
57
+ ```
58
+
59
+ ## API reference
60
+
61
+ ### Connection
62
+
63
+ ```python
64
+ client = LoomClient(url) # Create client
65
+ await client.connect() # Open WebSocket + start listener
66
+ await client.close() # Tear down
67
+ ```
68
+
69
+ ### Commands
70
+
71
+ All command methods are async and return typed Pydantic models.
72
+
73
+ #### `get_browser_state(record=None) -> ProjectedState`
74
+
75
+ Returns the current viewport state including locus, zoom level, loaded tracks, and ROIs.
76
+
77
+ ```python
78
+ state = await client.get_browser_state()
79
+ for track in state.tracks:
80
+ print(f"{track.name} ({track.type})")
81
+ ```
82
+
83
+ #### `navigate(locus=None, zoom=None, factor=None) -> bool`
84
+
85
+ Move the viewport to a genomic locus or zoom in/out.
86
+
87
+ ```python
88
+ await client.navigate(locus="chr1:1000-2000")
89
+ await client.navigate(zoom="in", factor=3.0)
90
+ ```
91
+
92
+ #### `modify_tracks(actions) -> ModifyTracksResult`
93
+
94
+ Add, remove, find, or update tracks in a single batch.
95
+
96
+ ```python
97
+ from loom_client import AddTrackAction, RemoveTrackAction, TrackSessionConfig, DataSourceConfig, WireTrackSelector
98
+
99
+ # Add a BigWig track
100
+ result = await client.modify_tracks([
101
+ AddTrackAction(config=TrackSessionConfig(
102
+ type="wig",
103
+ name="H3K27ac",
104
+ data_source=DataSourceConfig(
105
+ type="bigwig",
106
+ url="https://example.com/h3k27ac.bw",
107
+ ),
108
+ ))
109
+ ])
110
+
111
+ # Remove by name pattern
112
+ result = await client.modify_tracks([
113
+ RemoveTrackAction(selector=WireTrackSelector(name_regex="H3K.*"))
114
+ ])
115
+ ```
116
+
117
+ #### `query_features(track_id, summarize=None) -> QueryFeaturesResult`
118
+
119
+ Query features for a loaded track.
120
+
121
+ ```python
122
+ result = await client.query_features("track-id-123", summarize=True)
123
+ print(result.feature_count)
124
+ ```
125
+
126
+ #### `set_layout(tracks, locus=None) -> SetLayoutResult`
127
+
128
+ Replace the entire track layout at once.
129
+
130
+ ```python
131
+ result = await client.set_layout(
132
+ tracks=[
133
+ TrackSessionConfig(type="ruler"),
134
+ TrackSessionConfig(type="sequence"),
135
+ TrackSessionConfig(
136
+ type="annotation",
137
+ name="GENCODE",
138
+ data_source=DataSourceConfig(type="gencode", genome="hg38"),
139
+ ),
140
+ ],
141
+ locus="chr1:1000-5000",
142
+ )
143
+ ```
144
+
145
+ #### `export_view(format, width=None) -> str | SessionConfig`
146
+
147
+ Export the current view as SVG, PNG (returned as strings), or a session config object.
148
+
149
+ ```python
150
+ svg = await client.export_view("svg", width=1200)
151
+ session = await client.export_view("session") # returns SessionConfig
152
+ ```
153
+
154
+ #### `manage_rois(action) -> ROI result`
155
+
156
+ Manage regions of interest. The action type determines the operation.
157
+
158
+ ```python
159
+ from loom_client import AddROIAction, ROI, ListROIAction, FindROIAtLocusAction
160
+
161
+ # Add a region of interest
162
+ result = await client.manage_rois(AddROIAction(
163
+ roi=ROI(id="roi-1", chr="chr17", start=7565097, end=7590856,
164
+ name="TP53", color="rgba(255,0,0,0.3)"),
165
+ set_name="Genes of interest",
166
+ ))
167
+
168
+ # List all ROIs
169
+ result = await client.manage_rois(ListROIAction())
170
+
171
+ # Find ROIs overlapping a locus
172
+ result = await client.manage_rois(
173
+ FindROIAtLocusAction(chr="chr17", start=7500000, end=7600000)
174
+ )
175
+ ```
176
+
177
+ #### `subscribe_events(events) -> SubscribeEventsResult`
178
+
179
+ Subscribe to server-pushed events. Use `["*"]` for all events or `[]` to unsubscribe.
180
+
181
+ ```python
182
+ await client.subscribe_events(["locuschange", "trackadded", "dataloaded"])
183
+ ```
184
+
185
+ ### Event handling
186
+
187
+ Register callbacks for browser events:
188
+
189
+ ```python
190
+ def on_locus_change(data):
191
+ locus = data["locus"]
192
+ print(f"Moved to {locus['chr']}:{locus['start']}-{locus['end']}")
193
+
194
+ client.on("locuschange", on_locus_change)
195
+
196
+ # Wildcard handler receives (event_name, data)
197
+ client.on("*", lambda name, data: print(f"Event: {name}"))
198
+
199
+ # Unregister
200
+ client.off("locuschange", on_locus_change)
201
+ ```
202
+
203
+ **Available events:** `locuschange`, `trackadded`, `trackremoved`, `dataloaded`, `dataerror`, `rendererror`, `trackclick`, `trackhover`, `trackcontextmenu`, `trackorderchanged`, `roiadded`, `roiremoved`, `roichanged`, `roiclick`, `roicontextmenu`
204
+
205
+ ### Error handling
206
+
207
+ Server errors raise `CommandException`:
208
+
209
+ ```python
210
+ from loom_client import CommandException
211
+
212
+ try:
213
+ await client.navigate(locus="invalid")
214
+ except CommandException as e:
215
+ print(e.code, e.message)
216
+ ```
217
+
218
+ ## Development
219
+
220
+ ```bash
221
+ pip install -e ".[dev]"
222
+ pytest
223
+ ```
224
+
225
+ ## License
226
+
227
+ MIT
@@ -0,0 +1,215 @@
1
+ # loom-client
2
+
3
+ Python WebSocket client for the [Loom Genome Browser](https://github.com/nicholasrq/loom). Provides a fully async, typed interface for programmatically controlling a running Loom instance -- navigating loci, managing tracks and regions of interest, exporting views, and subscribing to browser events.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install loom-client
9
+ ```
10
+
11
+ Or install from source:
12
+
13
+ ```bash
14
+ git clone https://github.com/riyavsinha/loom-client.git
15
+ cd loom-client
16
+ pip install -e .
17
+ ```
18
+
19
+ Requires Python >= 3.11.
20
+
21
+ ## Quick start
22
+
23
+ ```python
24
+ import asyncio
25
+ from loom_client import LoomClient
26
+
27
+ async def main():
28
+ client = LoomClient("ws://localhost:8080")
29
+ await client.connect()
30
+
31
+ # Get current browser state
32
+ state = await client.get_browser_state()
33
+ print(state.locus_string) # e.g. "chr1:1,000-2,000"
34
+ print(state.zoom_level) # e.g. ZoomLevel.GENE
35
+
36
+ # Navigate to a locus
37
+ await client.navigate(locus="chr17:7565097-7590856") # TP53
38
+
39
+ # Zoom out 2x
40
+ await client.navigate(zoom="out", factor=2.0)
41
+
42
+ await client.close()
43
+
44
+ asyncio.run(main())
45
+ ```
46
+
47
+ ## API reference
48
+
49
+ ### Connection
50
+
51
+ ```python
52
+ client = LoomClient(url) # Create client
53
+ await client.connect() # Open WebSocket + start listener
54
+ await client.close() # Tear down
55
+ ```
56
+
57
+ ### Commands
58
+
59
+ All command methods are async and return typed Pydantic models.
60
+
61
+ #### `get_browser_state(record=None) -> ProjectedState`
62
+
63
+ Returns the current viewport state including locus, zoom level, loaded tracks, and ROIs.
64
+
65
+ ```python
66
+ state = await client.get_browser_state()
67
+ for track in state.tracks:
68
+ print(f"{track.name} ({track.type})")
69
+ ```
70
+
71
+ #### `navigate(locus=None, zoom=None, factor=None) -> bool`
72
+
73
+ Move the viewport to a genomic locus or zoom in/out.
74
+
75
+ ```python
76
+ await client.navigate(locus="chr1:1000-2000")
77
+ await client.navigate(zoom="in", factor=3.0)
78
+ ```
79
+
80
+ #### `modify_tracks(actions) -> ModifyTracksResult`
81
+
82
+ Add, remove, find, or update tracks in a single batch.
83
+
84
+ ```python
85
+ from loom_client import AddTrackAction, RemoveTrackAction, TrackSessionConfig, DataSourceConfig, WireTrackSelector
86
+
87
+ # Add a BigWig track
88
+ result = await client.modify_tracks([
89
+ AddTrackAction(config=TrackSessionConfig(
90
+ type="wig",
91
+ name="H3K27ac",
92
+ data_source=DataSourceConfig(
93
+ type="bigwig",
94
+ url="https://example.com/h3k27ac.bw",
95
+ ),
96
+ ))
97
+ ])
98
+
99
+ # Remove by name pattern
100
+ result = await client.modify_tracks([
101
+ RemoveTrackAction(selector=WireTrackSelector(name_regex="H3K.*"))
102
+ ])
103
+ ```
104
+
105
+ #### `query_features(track_id, summarize=None) -> QueryFeaturesResult`
106
+
107
+ Query features for a loaded track.
108
+
109
+ ```python
110
+ result = await client.query_features("track-id-123", summarize=True)
111
+ print(result.feature_count)
112
+ ```
113
+
114
+ #### `set_layout(tracks, locus=None) -> SetLayoutResult`
115
+
116
+ Replace the entire track layout at once.
117
+
118
+ ```python
119
+ result = await client.set_layout(
120
+ tracks=[
121
+ TrackSessionConfig(type="ruler"),
122
+ TrackSessionConfig(type="sequence"),
123
+ TrackSessionConfig(
124
+ type="annotation",
125
+ name="GENCODE",
126
+ data_source=DataSourceConfig(type="gencode", genome="hg38"),
127
+ ),
128
+ ],
129
+ locus="chr1:1000-5000",
130
+ )
131
+ ```
132
+
133
+ #### `export_view(format, width=None) -> str | SessionConfig`
134
+
135
+ Export the current view as SVG, PNG (returned as strings), or a session config object.
136
+
137
+ ```python
138
+ svg = await client.export_view("svg", width=1200)
139
+ session = await client.export_view("session") # returns SessionConfig
140
+ ```
141
+
142
+ #### `manage_rois(action) -> ROI result`
143
+
144
+ Manage regions of interest. The action type determines the operation.
145
+
146
+ ```python
147
+ from loom_client import AddROIAction, ROI, ListROIAction, FindROIAtLocusAction
148
+
149
+ # Add a region of interest
150
+ result = await client.manage_rois(AddROIAction(
151
+ roi=ROI(id="roi-1", chr="chr17", start=7565097, end=7590856,
152
+ name="TP53", color="rgba(255,0,0,0.3)"),
153
+ set_name="Genes of interest",
154
+ ))
155
+
156
+ # List all ROIs
157
+ result = await client.manage_rois(ListROIAction())
158
+
159
+ # Find ROIs overlapping a locus
160
+ result = await client.manage_rois(
161
+ FindROIAtLocusAction(chr="chr17", start=7500000, end=7600000)
162
+ )
163
+ ```
164
+
165
+ #### `subscribe_events(events) -> SubscribeEventsResult`
166
+
167
+ Subscribe to server-pushed events. Use `["*"]` for all events or `[]` to unsubscribe.
168
+
169
+ ```python
170
+ await client.subscribe_events(["locuschange", "trackadded", "dataloaded"])
171
+ ```
172
+
173
+ ### Event handling
174
+
175
+ Register callbacks for browser events:
176
+
177
+ ```python
178
+ def on_locus_change(data):
179
+ locus = data["locus"]
180
+ print(f"Moved to {locus['chr']}:{locus['start']}-{locus['end']}")
181
+
182
+ client.on("locuschange", on_locus_change)
183
+
184
+ # Wildcard handler receives (event_name, data)
185
+ client.on("*", lambda name, data: print(f"Event: {name}"))
186
+
187
+ # Unregister
188
+ client.off("locuschange", on_locus_change)
189
+ ```
190
+
191
+ **Available events:** `locuschange`, `trackadded`, `trackremoved`, `dataloaded`, `dataerror`, `rendererror`, `trackclick`, `trackhover`, `trackcontextmenu`, `trackorderchanged`, `roiadded`, `roiremoved`, `roichanged`, `roiclick`, `roicontextmenu`
192
+
193
+ ### Error handling
194
+
195
+ Server errors raise `CommandException`:
196
+
197
+ ```python
198
+ from loom_client import CommandException
199
+
200
+ try:
201
+ await client.navigate(locus="invalid")
202
+ except CommandException as e:
203
+ print(e.code, e.message)
204
+ ```
205
+
206
+ ## Development
207
+
208
+ ```bash
209
+ pip install -e ".[dev]"
210
+ pytest
211
+ ```
212
+
213
+ ## License
214
+
215
+ MIT
@@ -0,0 +1,149 @@
1
+ """loom_client — Python WebSocket client for the Loom Genome Browser."""
2
+
3
+ from .client import LoomClient
4
+ from .exceptions import CommandException
5
+ from .models import (
6
+ AddROIAction,
7
+ AddROIResult,
8
+ AddTrackAction,
9
+ AnnotationFeatureSummary,
10
+ ClearROIAction,
11
+ ClearROIResult,
12
+ CommandArgs,
13
+ DataSourceConfig,
14
+ ErrorDetail,
15
+ ErrorMessage,
16
+ EventMessage,
17
+ ExportViewArgs,
18
+ FindROIAtLocusAction,
19
+ FindROIAtLocusResult,
20
+ FindTrackAction,
21
+ GetBrowserStateArgs,
22
+ GetVisibleROIAction,
23
+ GetVisibleROIResult,
24
+ InteractionFeatureSummary,
25
+ ListROIAction,
26
+ ListROIResult,
27
+ ListROISetsAction,
28
+ ListROISetsResult,
29
+ Locus,
30
+ LocusChangeData,
31
+ ManageROIsArgs,
32
+ ManageROIsResult,
33
+ ModifyTrackAction,
34
+ ModifyTrackActionResult,
35
+ ModifyTracksArgs,
36
+ ModifyTracksResult,
37
+ NavigateArgs,
38
+ OutboundMessage,
39
+ ProjectedState,
40
+ QueryFeaturesArgs,
41
+ QueryFeaturesResult,
42
+ ROI,
43
+ ROIAction,
44
+ ROIEventData,
45
+ ROIInteractionEventData,
46
+ ROISetConfig,
47
+ RemoveROIAction,
48
+ RemoveROIResult,
49
+ RemoveTrackAction,
50
+ RequestMessage,
51
+ ResponseMessage,
52
+ SessionConfig,
53
+ SetLayoutArgs,
54
+ SetLayoutResult,
55
+ SignalRange,
56
+ SingleTrackEventData,
57
+ SubscribeEventsArgs,
58
+ SubscribeEventsResult,
59
+ TrackErrorData,
60
+ TrackFeatureSummary,
61
+ TrackInteractionData,
62
+ TrackOrderChangedData,
63
+ TrackSessionConfig,
64
+ TrackSummary,
65
+ UpdateMetadataAction,
66
+ UpdateROIAction,
67
+ UpdateROIResult,
68
+ WigFeatureSummary,
69
+ WireTrackSelector,
70
+ ZoomLevel,
71
+ )
72
+
73
+ __all__ = [
74
+ "LoomClient",
75
+ "CommandException",
76
+ # Wire messages
77
+ "RequestMessage",
78
+ "ResponseMessage",
79
+ "ErrorDetail",
80
+ "ErrorMessage",
81
+ "EventMessage",
82
+ "OutboundMessage",
83
+ # Core genomic types
84
+ "Locus",
85
+ "ROI",
86
+ "ROISetConfig",
87
+ # Track session configs
88
+ "DataSourceConfig",
89
+ "TrackSessionConfig",
90
+ "SessionConfig",
91
+ # State projection
92
+ "ZoomLevel",
93
+ "SignalRange",
94
+ "WigFeatureSummary",
95
+ "AnnotationFeatureSummary",
96
+ "InteractionFeatureSummary",
97
+ "TrackFeatureSummary",
98
+ "TrackSummary",
99
+ "ProjectedState",
100
+ # Track selector
101
+ "WireTrackSelector",
102
+ # Command args
103
+ "CommandArgs",
104
+ "GetBrowserStateArgs",
105
+ "NavigateArgs",
106
+ "AddTrackAction",
107
+ "RemoveTrackAction",
108
+ "FindTrackAction",
109
+ "UpdateMetadataAction",
110
+ "ModifyTrackAction",
111
+ "ModifyTracksArgs",
112
+ "QueryFeaturesArgs",
113
+ "SetLayoutArgs",
114
+ "ExportViewArgs",
115
+ "AddROIAction",
116
+ "RemoveROIAction",
117
+ "UpdateROIAction",
118
+ "ClearROIAction",
119
+ "ListROIAction",
120
+ "ListROISetsAction",
121
+ "FindROIAtLocusAction",
122
+ "GetVisibleROIAction",
123
+ "ROIAction",
124
+ "ManageROIsArgs",
125
+ "SubscribeEventsArgs",
126
+ # Command results
127
+ "ModifyTrackActionResult",
128
+ "ModifyTracksResult",
129
+ "QueryFeaturesResult",
130
+ "SetLayoutResult",
131
+ "AddROIResult",
132
+ "RemoveROIResult",
133
+ "UpdateROIResult",
134
+ "ClearROIResult",
135
+ "ListROIResult",
136
+ "ListROISetsResult",
137
+ "FindROIAtLocusResult",
138
+ "GetVisibleROIResult",
139
+ "ManageROIsResult",
140
+ "SubscribeEventsResult",
141
+ # Event data
142
+ "LocusChangeData",
143
+ "SingleTrackEventData",
144
+ "TrackOrderChangedData",
145
+ "TrackErrorData",
146
+ "TrackInteractionData",
147
+ "ROIEventData",
148
+ "ROIInteractionEventData",
149
+ ]