threadlepy 0.1.2__tar.gz → 0.1.4__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.
Files changed (29) hide show
  1. {threadlepy-0.1.2 → threadlepy-0.1.4}/CHANGELOG.md +11 -0
  2. {threadlepy-0.1.2/src/threadlepy.egg-info → threadlepy-0.1.4}/PKG-INFO +1 -1
  3. {threadlepy-0.1.2 → threadlepy-0.1.4}/pyproject.toml +1 -1
  4. threadlepy-0.1.4/scripts/test_all_commands_example_data.py +360 -0
  5. {threadlepy-0.1.2 → threadlepy-0.1.4}/src/threadlepy/__init__.py +1 -1
  6. {threadlepy-0.1.2 → threadlepy-0.1.4}/src/threadlepy/commands.py +35 -10
  7. {threadlepy-0.1.2 → threadlepy-0.1.4/src/threadlepy.egg-info}/PKG-INFO +1 -1
  8. {threadlepy-0.1.2 → threadlepy-0.1.4}/src/threadlepy.egg-info/SOURCES.txt +1 -0
  9. {threadlepy-0.1.2 → threadlepy-0.1.4}/LICENSE +0 -0
  10. {threadlepy-0.1.2 → threadlepy-0.1.4}/MANIFEST.in +0 -0
  11. {threadlepy-0.1.2 → threadlepy-0.1.4}/README.md +0 -0
  12. {threadlepy-0.1.2 → threadlepy-0.1.4}/docs/threadlepy_tutorial.ipynb +0 -0
  13. {threadlepy-0.1.2 → threadlepy-0.1.4}/scripts/test_commands.py +0 -0
  14. {threadlepy-0.1.2 → threadlepy-0.1.4}/scripts/test_session.py +0 -0
  15. {threadlepy-0.1.2 → threadlepy-0.1.4}/setup.cfg +0 -0
  16. {threadlepy-0.1.2 → threadlepy-0.1.4}/src/threadlepy/Examples/Scripts/create.txt +0 -0
  17. {threadlepy-0.1.2 → threadlepy-0.1.4}/src/threadlepy/Examples/dscw.tsv +0 -0
  18. {threadlepy-0.1.2 → threadlepy-0.1.4}/src/threadlepy/Examples/dscw_nodeset.tsv +0 -0
  19. {threadlepy-0.1.2 → threadlepy-0.1.4}/src/threadlepy/Examples/lazega.tsv +0 -0
  20. {threadlepy-0.1.2 → threadlepy-0.1.4}/src/threadlepy/Examples/lazega_female.tsv +0 -0
  21. {threadlepy-0.1.2 → threadlepy-0.1.4}/src/threadlepy/Examples/lazega_female_nodeset.tsv +0 -0
  22. {threadlepy-0.1.2 → threadlepy-0.1.4}/src/threadlepy/Examples/lazega_nodes.tsv +0 -0
  23. {threadlepy-0.1.2 → threadlepy-0.1.4}/src/threadlepy/Examples/mynet.tsv +0 -0
  24. {threadlepy-0.1.2 → threadlepy-0.1.4}/src/threadlepy/Examples/mynet_nodesetfile.tsv +0 -0
  25. {threadlepy-0.1.2 → threadlepy-0.1.4}/src/threadlepy/Examples/mynodes.tsv +0 -0
  26. {threadlepy-0.1.2 → threadlepy-0.1.4}/src/threadlepy/client.py +0 -0
  27. {threadlepy-0.1.2 → threadlepy-0.1.4}/src/threadlepy/py.typed +0 -0
  28. {threadlepy-0.1.2 → threadlepy-0.1.4}/src/threadlepy.egg-info/dependency_links.txt +0 -0
  29. {threadlepy-0.1.2 → threadlepy-0.1.4}/src/threadlepy.egg-info/top_level.txt +0 -0
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.4
4
+
5
+ - Added the `return_histograms` option to `rwfpt()` / `th_rwfpt()` to match the current threadleR wrapper.
6
+
7
+ ## 0.1.3
8
+
9
+ - Aligned `load_examples()` with threadleR behavior by assigning example handles by default and restoring the previous Threadle working directory after loading.
10
+ - Added regression coverage for `load_examples()` workdir restoration and assignment behavior.
11
+ - Added `scripts/test_all_commands_example_data.py`, an end-to-end workflow modeled after the threadleR example-data command script.
12
+ - Ignored generated `threadle_examples/` and `threadle_outputs/` directories.
13
+
3
14
  ## 0.1.2
4
15
 
5
16
  - Added one-line docstrings for public command wrappers.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: threadlepy
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Python client for the Threadle CLI JSON interface.
5
5
  Author: Yukun Jiao
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "threadlepy"
7
- version = "0.1.2"
7
+ version = "0.1.4"
8
8
  description = "Python client for the Threadle CLI JSON interface."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -0,0 +1,360 @@
1
+ #!/usr/bin/env python3
2
+ """End-to-end threadlepy command workflow using bundled example data.
3
+
4
+ Usage:
5
+ python scripts/test_all_commands_example_data.py
6
+ python scripts/test_all_commands_example_data.py --threadle /full/path/to/threadle
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import sys
13
+ from pathlib import Path
14
+ from typing import Any, Callable
15
+
16
+ ROOT = Path(__file__).resolve().parents[1]
17
+ SRC = ROOT / "src"
18
+ if str(SRC) not in sys.path:
19
+ sys.path.insert(0, str(SRC))
20
+
21
+ import threadlepy
22
+ import threadlepy.commands as th
23
+
24
+
25
+ def show(label: str, fn: Callable[[], Any]) -> Any:
26
+ print(f"\n>>> {label}", flush=True)
27
+ value = fn()
28
+ if value is not None:
29
+ text = repr(value)
30
+ if len(text) > 1000:
31
+ text = text[:997] + "..."
32
+ print(text, flush=True)
33
+ else:
34
+ print("None", flush=True)
35
+ return value
36
+
37
+
38
+ def run(threadle_path: str | None = None, *, timeout: float = 1800.0) -> int:
39
+ path = threadle_path or "threadle"
40
+ if not threadlepy.is_available(path):
41
+ print(
42
+ "SKIP: Threadle CLI was not found. Install `threadle`, add it to PATH, "
43
+ "or pass --threadle /full/path/to/threadle.",
44
+ file=sys.stderr,
45
+ )
46
+ return 77
47
+
48
+ root_dir = Path.cwd()
49
+ example_dir = root_dir / "threadle_examples"
50
+ outputs_dir = root_dir / "threadle_outputs"
51
+ example_dir.mkdir(parents=True, exist_ok=True)
52
+ outputs_dir.mkdir(parents=True, exist_ok=True)
53
+
54
+ threadlepy.configure(print_commands=False, print_messages=False, timeout=timeout)
55
+
56
+ show("stage examples", lambda: th.stage_examples_to_wd(str(example_dir), overwrite=True))
57
+ show("is_available", lambda: threadlepy.is_available(path))
58
+
59
+ threadlepy.start(threadle_path)
60
+ try:
61
+ show("python cwd", Path.cwd)
62
+ show("threadle initial workdir", th.get_workdir)
63
+
64
+ # Load bundled example data from the staged example folder.
65
+ show("set workdir to staged examples", lambda: th.set_workdir(str(example_dir)))
66
+ mynet_nodeset = show(
67
+ "load mynet_nodeset",
68
+ lambda: th.load_file("mynet_nodeset", "mynet_nodesetfile.tsv", type="nodeset"),
69
+ )
70
+ mynet = show("load mynet", lambda: th.load_file("mynet", "mynet.tsv", type="network"))
71
+ lazega_nodeset = show(
72
+ "load lazega_nodeset",
73
+ lambda: th.load_file("lazega_nodeset", "lazega_nodes.tsv", type="nodeset"),
74
+ )
75
+ lazega = show("load lazega", lambda: th.load_file("lazega", "lazega.tsv", type="network"))
76
+ example_data = {
77
+ "mynet_nodeset": mynet_nodeset,
78
+ "mynet": mynet,
79
+ "lazega_nodeset": lazega_nodeset,
80
+ "lazega": lazega,
81
+ }
82
+ show("example data names", lambda: list(example_data))
83
+ show("threadle staged workdir", th.get_workdir)
84
+ show("dir", th.dir)
85
+
86
+ show("inventory", th.i)
87
+ show("info mynet", lambda: th.info(mynet))
88
+ show("preview mynet", lambda: th.preview(mynet))
89
+ show("raw cmd info", lambda: th.cmd("info", args={"structure": mynet}))
90
+
91
+ # Explore nodes, attributes, one-mode ties, and two-mode affiliations.
92
+ show("mynet node count", lambda: th.get_nbr_nodes(mynet))
93
+ show("mynet nodes", lambda: th.get_all_nodes(mynet, offset=0, limit=8))
94
+ show("first node", lambda: th.get_nodeid_by_index(mynet, index=0))
95
+ show("random node", lambda: th.get_random_node(mynet))
96
+ show("gender 123", lambda: th.get_attr(mynet_nodeset, nodeid=123, attrname="gender"))
97
+ show("some genders", lambda: th.get_attrs(mynet_nodeset, nodes=[123, 234, 345], attrname="gender"))
98
+ show("gender summary", lambda: th.get_attr_summary(mynet_nodeset, attrname="gender"))
99
+
100
+ show("trade 123 345", lambda: th.get_edge(mynet, "trade", node1id=123, node2id=345))
101
+ show("has kinship", lambda: th.check_edge(mynet, "kinship", node1id=123, node2id=345))
102
+ show("trade edges", lambda: th.get_all_edges(mynet, "trade", offset=0, limit=10))
103
+ show("random trade edge", lambda: th.get_random_edge(mynet, "trade"))
104
+ show(
105
+ "alters 345",
106
+ lambda: th.get_node_alters(mynet, nodeid=345, layernames="trade", direction="both"),
107
+ )
108
+ show(
109
+ "degree 345",
110
+ lambda: th.get_degree(mynet, nodeid=345, layernames="trade", direction="both"),
111
+ )
112
+ show(
113
+ "random alter 345",
114
+ lambda: th.get_random_alter(mynet, nodeid=345, layernames="trade", direction="both"),
115
+ )
116
+ show("density kinship", lambda: th.density(mynet, "kinship"))
117
+
118
+ show("work hyperedges", lambda: th.get_all_hyperedges(mynet, "work", offset=0, limit=10))
119
+ show("ias nodes", lambda: th.get_hyperedge_nodes(mynet, "work", hypername="ias"))
120
+ show("node 123 hyperedges", lambda: th.get_node_hyperedges(mynet, "work", nodeid=123))
121
+
122
+ # Derive structures from the example data.
123
+ female_nodeset = show(
124
+ "filter female nodeset",
125
+ lambda: th.filter("female_nodeset", mynet_nodeset, "gender", cond="eq", attrvalue="f"),
126
+ )
127
+ female_mynet = show("subnet female mynet", lambda: th.subnet("female_mynet", mynet, female_nodeset))
128
+ show("info female nodeset", lambda: th.info(female_nodeset))
129
+ show("info female mynet", lambda: th.info(female_mynet))
130
+
131
+ show("components kinship", lambda: th.components(mynet, "kinship", attrname="kinship_component"))
132
+ show("degree kinship", lambda: th.degree(mynet, "kinship", attrname="kinship_degree", direction="both"))
133
+ show("kinship degree summary", lambda: th.get_attr_summary(mynet, "kinship_degree"))
134
+
135
+ show(
136
+ "shortest path 123 to 567",
137
+ lambda: th.shortest_path(mynet, node1id=123, node2id=567, layernames="kinship"),
138
+ )
139
+ gender_shortest_paths = show(
140
+ "shortest paths by gender",
141
+ lambda: th.shortest_paths("gender_shortest_paths", mynet, attrname="gender", layernames="kinship"),
142
+ )
143
+ show("info gender shortest paths", lambda: th.info(gender_shortest_paths))
144
+
145
+ show("symmetrize trade", lambda: th.symmetrize(mynet, "trade", method="max", newlayername="trade_sym"))
146
+ show(
147
+ "dichotomize trade",
148
+ lambda: th.dichotomize(
149
+ mynet,
150
+ "trade",
151
+ cond="ge",
152
+ threshold=1000,
153
+ truevalue=1,
154
+ falsevalue=0,
155
+ newlayername="trade_hi",
156
+ ),
157
+ )
158
+ show("project work", lambda: th.project_two_mode(mynet, "work", method="count", newlayername="work_projected"))
159
+ show("trade_sym edge", lambda: th.get_edge(mynet, "trade_sym", 123, 345))
160
+ show("trade_hi edge", lambda: th.get_edge(mynet, "trade_hi", 890, 234))
161
+ show("work_projected edge", lambda: th.get_edge(mynet, "work_projected", 123, 456))
162
+
163
+ show("pack kinship", lambda: th.pack(mynet, layername="kinship"))
164
+ show("unpack kinship", lambda: th.unpack(mynet, layername="kinship"))
165
+
166
+ # Build and edit a small network.
167
+ lab_nodes = show("create lab nodes", lambda: th.create_nodeset("lab_nodes", name="Lab nodes", createnodes=5))
168
+ lab_net = show("create lab net", lambda: th.create_network("lab_net", lab_nodes, name="Lab network"))
169
+
170
+ show("add node 10", lambda: th.add_node(lab_nodes, nodeid=10))
171
+ show("lab nodes after add", lambda: th.get_all_nodes(lab_nodes, offset=0, limit=20))
172
+ show("remove node 10", lambda: th.remove_node(lab_nodes, nodeid=10))
173
+
174
+ show("add friendship", lambda: th.add_layer(lab_net, "friendship", mode=1, directed=False, valuetype="binary", selfties=False))
175
+ show("add events", lambda: th.add_layer(lab_net, "events", mode=2))
176
+ show("add edge 1 2", lambda: th.add_edge(lab_net, "friendship", node1id=1, node2id=2))
177
+ show("add edge 2 3", lambda: th.add_edge(lab_net, "friendship", node1id=2, node2id=3))
178
+ show("check friendship edge", lambda: th.check_edge(lab_net, "friendship", 1, 2))
179
+ show("friendship edges", lambda: th.get_all_edges(lab_net, "friendship", offset=0, limit=10))
180
+ show("remove edge 2 3", lambda: th.remove_edge(lab_net, "friendship", node1id=2, node2id=3))
181
+
182
+ show("add edge 3 4", lambda: th.add_edge(lab_net, "friendship", node1id=3, node2id=4))
183
+ show("random friendship edge", lambda: th.get_random_edge(lab_net, "friendship"))
184
+ show("clear friendship", lambda: th.clear_layer(lab_net, "friendship"))
185
+ show("friendship edges after clear", lambda: th.get_all_edges(lab_net, "friendship", offset=0, limit=10))
186
+
187
+ show("add seminar hyperedge", lambda: th.add_hyper(lab_net, "events", hypername="seminar", nodes=[1, 2]))
188
+ show("add seminar affiliation", lambda: th.add_aff(lab_net, "events", nodeid=3, hypername="seminar"))
189
+ show("seminar nodes", lambda: th.get_hyperedge_nodes(lab_net, "events", "seminar"))
190
+ show("node 3 hyperedges", lambda: th.get_node_hyperedges(lab_net, "events", 3))
191
+ show("remove seminar affiliation", lambda: th.remove_aff(lab_net, "events", nodeid=3, hypername="seminar"))
192
+ show("remove seminar", lambda: th.remove_hyper(lab_net, "events", hypername="seminar"))
193
+
194
+ show("add temporary layer", lambda: th.add_layer(lab_net, "temporary", mode=1))
195
+ show("remove temporary layer", lambda: th.remove_layer(lab_net, "temporary"))
196
+
197
+ show("define score", lambda: th.define_attr(lab_nodes, "score", "int"))
198
+ show("set score", lambda: th.set_attr(lab_nodes, nodeid=1, attrname="score", attrvalue=7))
199
+ show("get score", lambda: th.get_attr(lab_nodes, 1, "score"))
200
+ show("remove score", lambda: th.remove_attr(lab_nodes, nodeid=1, attrname="score"))
201
+ show("undefine score", lambda: th.undefine_attr(lab_nodes, "score"))
202
+
203
+ show("generate random_score", lambda: th.generate_attr(lab_nodes, "random_score", attrtype="int", min=1, max=10))
204
+ show("random_score summary", lambda: th.get_attr_summary(lab_nodes, "random_score"))
205
+
206
+ # Generate a random layer and run random-walk commands.
207
+ random_nodes = show("create random nodes", lambda: th.create_nodeset("random_nodes", createnodes=20))
208
+ random_net = show("create random net", lambda: th.create_network("random_net", random_nodes))
209
+ show("add er_layer", lambda: th.add_layer(random_net, "er_layer", mode=1, directed=False, valuetype="binary"))
210
+ show("generate er_layer", lambda: th.generate(random_net, "er_layer", type="er", p=1))
211
+ show("er_layer edges", lambda: th.get_all_edges(random_net, "er_layer", offset=0, limit=10))
212
+
213
+ walk_nodes = show("create walk nodes", lambda: th.create_nodeset("walk_nodes", createnodes=6))
214
+ walk_net = show("create walk net", lambda: th.create_network("walk_net", walk_nodes))
215
+ show("add walk layer", lambda: th.add_layer(walk_net, "walk_layer", mode=1, directed=False, valuetype="binary"))
216
+ for node1, node2 in [(1, 2), (2, 3), (3, 4), (4, 5)]:
217
+ show(f"add walk edge {node1}-{node2}", lambda node1=node1, node2=node2: th.add_edge(walk_net, "walk_layer", node1, node2))
218
+ show("define group", lambda: th.define_attr(walk_net, "group", "int"))
219
+ for node in range(1, 6):
220
+ show(
221
+ f"set group {node}",
222
+ lambda node=node: th.set_attr(walk_net, nodeid=node, attrname="group", attrvalue=1 if node <= 2 else 2),
223
+ )
224
+
225
+ rw_distances = show(
226
+ "rw distances",
227
+ lambda: th.rwdistances(
228
+ "rw_distances",
229
+ walk_net,
230
+ attrname="group",
231
+ maxsteps=30,
232
+ layernames="walk_layer",
233
+ walkfactor=0.5,
234
+ balanced=False,
235
+ weighted=False,
236
+ backtrack=False,
237
+ savesteps=False,
238
+ ),
239
+ )
240
+ rw_fpt = show(
241
+ "rw fpt",
242
+ lambda: th.rwfpt(
243
+ "rw_fpt",
244
+ walk_net,
245
+ attrname="group",
246
+ maxsteps=30,
247
+ layernames="walk_layer",
248
+ walkfactor=0.5,
249
+ minpairobs=1,
250
+ balanced=False,
251
+ weighted=False,
252
+ ),
253
+ )
254
+ show("info rw distances", lambda: th.info(rw_distances))
255
+ show("info rw fpt", lambda: th.info(rw_fpt))
256
+ rw_fpt_hist = show(
257
+ "rw fpt with histograms",
258
+ lambda: th.rwfpt(
259
+ "rw_fpt_hist",
260
+ walk_net,
261
+ attrname="group",
262
+ maxsteps=30,
263
+ layernames="walk_layer",
264
+ walkfactor=0.5,
265
+ minpairobs=1,
266
+ balanced=False,
267
+ weighted=False,
268
+ return_histograms=True,
269
+ ),
270
+ )
271
+ show(
272
+ "info rw fpt histogram network",
273
+ lambda: th.info(rw_fpt_hist["network"] if isinstance(rw_fpt_hist, dict) else rw_fpt_hist),
274
+ )
275
+
276
+ # Miscellaneous session helpers.
277
+ show("setting verbose false", lambda: th.setting("verbose", False))
278
+ show("setting verbose string false", lambda: th.setting("verbose", "false"))
279
+ show("setting nodecache true", lambda: th.setting("nodecache", True))
280
+ show("setting nodecache false", lambda: th.setting("nodecache", False))
281
+ show("random seed", lambda: th.random_seed(42))
282
+ show("sync wd", th.sync_wd)
283
+
284
+ # Export, save, import, and load a Threadle script.
285
+ kinship_edges_file = outputs_dir / "mynet_kinship_edges.tsv"
286
+ mynet_gexf_file = outputs_dir / "mynet_kinship.gexf"
287
+ mynet_saved_file = outputs_dir / "mynet_saved.tsv"
288
+
289
+ show("export kinship layer", lambda: th.export_layer(mynet, "kinship", file=str(kinship_edges_file), header=True, sep="\t"))
290
+ show("export kinship gexf", lambda: th.export(mynet, format="gexf", file=str(mynet_gexf_file), layername="kinship"))
291
+ show("save mynet", lambda: th.save_file(mynet, file=str(mynet_saved_file)))
292
+ for file in (kinship_edges_file, mynet_gexf_file, mynet_saved_file):
293
+ if not file.exists():
294
+ raise FileNotFoundError(f"Expected output file was not created: {file}")
295
+
296
+ mynet_nodeset_copy = show(
297
+ "load mynet nodeset copy",
298
+ lambda: th.load_file("mynet_nodeset_copy", str(example_dir / "mynet_nodesetfile.tsv"), type="nodeset"),
299
+ )
300
+ show("info mynet nodeset copy", lambda: th.info(mynet_nodeset_copy))
301
+
302
+ import_nodes = show("create import nodes", lambda: th.create_nodeset("import_nodes", createnodes=3))
303
+ import_net = show("create import net", lambda: th.create_network("import_net", import_nodes))
304
+ show("add imported_edges layer", lambda: th.add_layer(import_net, "imported_edges", mode=1, directed=False, valuetype="valued"))
305
+ edge_file = outputs_dir / "import_edges.tsv"
306
+ edge_file.write_text("1\t2\t4\n2\t3\t5\n", encoding="utf-8")
307
+ show(
308
+ "import edges",
309
+ lambda: th.import_layer(
310
+ import_net,
311
+ "imported_edges",
312
+ file=str(edge_file),
313
+ format="edgelist",
314
+ node1col=0,
315
+ node2col=1,
316
+ valuecol=2,
317
+ header=False,
318
+ sep="\t",
319
+ addmissingnodes=False,
320
+ ),
321
+ )
322
+ show("imported edge value", lambda: th.get_edge(import_net, "imported_edges", 1, 2))
323
+
324
+ script_file = outputs_dir / "threadle_script.txt"
325
+ script_file.write_text(
326
+ "\n".join(
327
+ [
328
+ "script_nodes = createnodeset(name=script_nodes, createnodes=3)",
329
+ "script_net = createnetwork(name=script_net, nodeset=script_nodes)",
330
+ "addlayer(network=script_net, layername=script_layer, mode=1, directed=false, valuetype=binary, selfties=false)",
331
+ ]
332
+ )
333
+ + "\n",
334
+ encoding="utf-8",
335
+ )
336
+ show("load threadle script", lambda: th.load_script(str(script_file)))
337
+ show("info script net", lambda: th.info("script_net"))
338
+
339
+ delete_me = show("create delete_me", lambda: th.create_nodeset("delete_me", createnodes=1))
340
+ show("delete delete_me", lambda: th.delete(delete_me))
341
+ show("inventory before delete_all", th.i)
342
+ show("delete all", th.delete_all)
343
+ show("inventory after delete_all", th.i)
344
+ finally:
345
+ threadlepy.stop()
346
+
347
+ print(f"\nWorkflow complete. Output directory: {outputs_dir}")
348
+ return 0
349
+
350
+
351
+ def main() -> int:
352
+ parser = argparse.ArgumentParser(description=__doc__)
353
+ parser.add_argument("--threadle", default=None, help="Path to the Threadle CLI executable.")
354
+ parser.add_argument("--timeout", type=float, default=1800.0, help="Threadle response timeout in seconds.")
355
+ args = parser.parse_args()
356
+ return run(args.threadle, timeout=args.timeout)
357
+
358
+
359
+ if __name__ == "__main__":
360
+ raise SystemExit(main())
@@ -2,7 +2,7 @@
2
2
 
3
3
  from .client import ThreadleStruct, configure, is_available, raw_cmd, session, start, stop
4
4
 
5
- __version__ = "0.1.2"
5
+ __version__ = "0.1.4"
6
6
 
7
7
  __all__ = [
8
8
  "ThreadleStruct",
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import os
4
+ import inspect
4
5
  import shutil
5
6
  from importlib import resources
6
7
  from typing import Any, Literal, Optional, Union
@@ -597,7 +598,7 @@ def load_script(file: str):
597
598
 
598
599
  def load_examples(
599
600
  examples: Union[str, list[str], tuple[str, ...]] = ("mynet", "lazega"),
600
- assign: bool = False,
601
+ assign: bool = True,
601
602
  set_workdir_to_examples: bool = True,
602
603
  set_workdir: Optional[bool] = None,
603
604
  ):
@@ -623,15 +624,29 @@ def load_examples(
623
624
  if unknown:
624
625
  raise ValueError(f"Unknown example dataset(s): {', '.join(unknown)}")
625
626
 
627
+ caller_frame = inspect.currentframe().f_back
628
+ old_workdir = None
629
+
626
630
  with resources.as_file(resources.files("threadlepy").joinpath("Examples")) as ext:
627
631
  if set_workdir_to_examples:
632
+ old_workdir = get_workdir()
628
633
  globals()["set_workdir"](str(ext))
629
- out: dict[str, ThreadleStruct] = {}
630
- for example in selected:
631
- ns_name, ns_file, net_name, net_file = specs[example]
632
- out[ns_name] = load_file(ns_name, os.path.join(str(ext), ns_file), type="nodeset")
633
- out[net_name] = load_file(net_name, os.path.join(str(ext), net_file), type="network")
634
- return out
634
+
635
+ try:
636
+ out: dict[str, ThreadleStruct] = {}
637
+ for example in selected:
638
+ ns_name, ns_file, net_name, net_file = specs[example]
639
+ out[ns_name] = load_file(ns_name, os.path.join(str(ext), ns_file), type="nodeset")
640
+ out[net_name] = load_file(net_name, os.path.join(str(ext), net_file), type="network")
641
+
642
+ if assign and caller_frame is not None:
643
+ caller_frame.f_globals.update(out)
644
+ caller_frame.f_locals.update(out)
645
+
646
+ return out
647
+ finally:
648
+ if old_workdir is not None:
649
+ globals()["set_workdir"](old_workdir)
635
650
 
636
651
 
637
652
  def export(network: ThreadleName, file: str, layername: str, format: str = "gexf"):
@@ -720,14 +735,21 @@ def rwfpt(
720
735
  minpairobs: int = 10,
721
736
  balanced: bool = False,
722
737
  weighted: bool = False,
738
+ return_histograms: bool = False,
723
739
  layername: Optional[Union[str, list[str], tuple[str, ...]]] = None,
724
740
  ):
725
741
  """Estimate category mean first-passage times with random walks and return a result network handle."""
726
742
  if layername is not None and not layernames:
727
743
  layernames = layername
728
744
  layernames = _collapse(layernames)
729
- call("rwfpt", locals(), assign=name, drop=("layername",))
730
- return _handle(name)
745
+ args = locals().copy()
746
+ if return_histograms:
747
+ args["returnhistograms"] = "true"
748
+ payload = call("rwfpt", args, assign=name, drop=("layername", "return_histograms"))
749
+ network_handle = _handle(name)
750
+ if return_histograms and payload is not None:
751
+ return {"network": network_handle, "histograms": payload}
752
+ return network_handle
731
753
 
732
754
 
733
755
  def random_seed(seed: int = 6031769):
@@ -740,7 +762,10 @@ def setting(name: str, value: Any):
740
762
  if name.lower() == "verbose":
741
763
  from . import client
742
764
 
743
- client.configure(print_messages=bool(value))
765
+ if isinstance(value, str):
766
+ client.configure(print_messages=value.strip().lower() != "false")
767
+ else:
768
+ client.configure(print_messages=bool(value))
744
769
  return None
745
770
  return call("setting", locals())
746
771
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: threadlepy
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Python client for the Threadle CLI JSON interface.
5
5
  Author: Yukun Jiao
6
6
  License-Expression: MIT
@@ -4,6 +4,7 @@ MANIFEST.in
4
4
  README.md
5
5
  pyproject.toml
6
6
  docs/threadlepy_tutorial.ipynb
7
+ scripts/test_all_commands_example_data.py
7
8
  scripts/test_commands.py
8
9
  scripts/test_session.py
9
10
  src/threadlepy/__init__.py
File without changes
File without changes
File without changes
File without changes