napistu 0.3.4__py3-none-any.whl → 0.3.6__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.
napistu/source.py CHANGED
@@ -223,7 +223,7 @@ def greedy_set_coverge_of_sources(
223
223
  Greedy Set Coverage of Sources
224
224
 
225
225
  Apply the greedy set coverge algorithm to find the minimal set of
226
- sources which cover all entries
226
+ sources which cover all entries
227
227
 
228
228
  Parameters:
229
229
  source_df: pd.DataFrame
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: napistu
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: Connecting high-dimensional data to curated pathways
5
5
  Home-page: https://github.com/napistu/napistu-py
6
6
  Author: Sean Hackett
@@ -43,7 +43,7 @@ Requires-Dist: pytest-cov; extra == "dev"
43
43
  Requires-Dist: ruff; extra == "dev"
44
44
  Requires-Dist: testcontainers; extra == "dev"
45
45
  Provides-Extra: mcp
46
- Requires-Dist: fastmcp<3.0.0,>=2.0.0; extra == "mcp"
46
+ Requires-Dist: fastmcp<2.9.0,>=2.0.0; extra == "mcp"
47
47
  Requires-Dist: mcp<2.0.0,>=1.0.0; extra == "mcp"
48
48
  Requires-Dist: httpx>=0.24.0; extra == "mcp"
49
49
  Requires-Dist: beautifulsoup4<5.0.0,>=4.11.0; extra == "mcp"
@@ -1,22 +1,22 @@
1
1
  napistu/__init__.py,sha256=dFXAhIqlTLJMwowS4BUDT08-Vy3Q0u1L0CMCErSZT1Y,239
2
- napistu/__main__.py,sha256=PbzIsFAoFHNQuSyi-ql-D7tQLEOuqgmTcgk0PY-OGeU,28636
3
- napistu/consensus.py,sha256=UbKKSLP1O46e3Rk8d_aqNlhRHeR3sZRztAgIm7-XK6Y,69960
4
- napistu/constants.py,sha256=10SuKl19koWvCaeM2k-5GDQxvJpKY3GaGFY_4VZivXw,12356
5
- napistu/identifiers.py,sha256=wsVriQdvPllA5uvh5CiREklA2tYW2MIB14dV7CPaMVU,34003
2
+ napistu/__main__.py,sha256=JnPRdK9Revwpi0pouC7ZIZAMEunh9NIFuaq91RlPaR0,28648
3
+ napistu/consensus.py,sha256=jsWpoXOzmNklFKasgOa5NEqbhoZvsgY0rLMFQddi6Vo,69945
4
+ napistu/constants.py,sha256=IhjZZgP0Yb3pPn5mV5SeTWiimRSG2hH02UX_Y6c_aOo,12388
5
+ napistu/identifiers.py,sha256=e2-nTVzr5AINa0y1ER9218bKXyF2kAeJ9At22S4Z00o,33914
6
6
  napistu/indices.py,sha256=E_djN1XWc6l1lrFw_QnQXfZTKYTaUv8-jFPP7cHkY5A,9780
7
- napistu/sbml_dfs_core.py,sha256=w4hoggMAXJ4Np45_2j-l92vOZg134eYa5Sg7SIdCaFo,91804
8
- napistu/sbml_dfs_utils.py,sha256=LJo6WWTrmnE58ZLDuibeeHk88uCdfunWdja7XxdZpps,11525
9
- napistu/source.py,sha256=9uUJrkY4jHaKlzz5nNcQQ8wUAep2pfqhlHxHw1hmEkI,13648
7
+ napistu/sbml_dfs_core.py,sha256=ntZfuKrjf42MV1EXhKUro298a1aSd6t6M-tt141x634,71129
8
+ napistu/sbml_dfs_utils.py,sha256=aXbQIlx7Yv7TH_7YxSuHZLsf6w2UXd_iOzpALKzg608,43506
9
+ napistu/source.py,sha256=UGpN70bqbC9gnKmM0ivSdQYim9hfzgABeXoQKzRr9oU,13646
10
10
  napistu/utils.py,sha256=TcholWrFbRSu_sn9ODMA8y2YyAhekEKZjwf4S0WQNzI,33241
11
11
  napistu/context/__init__.py,sha256=LQBEqipcHKK0E5UlDEg1ct-ymCs93IlUrUaH8BCevf0,242
12
12
  napistu/context/discretize.py,sha256=Qq7zg46F_I-PvQIT2_pEDQV7YEtUQCxKoRvT5Gu9QsE,15052
13
- napistu/context/filtering.py,sha256=oH1uNsf-_qSWRFtoxR8gDvCfZOzO3Ps40YO0qGAGXXs,13660
13
+ napistu/context/filtering.py,sha256=l1oq-43ysSGqU9VmhTOO_pYT4DSMf20yxvktPC1MI0I,13696
14
14
  napistu/gcs/__init__.py,sha256=dFXAhIqlTLJMwowS4BUDT08-Vy3Q0u1L0CMCErSZT1Y,239
15
15
  napistu/gcs/constants.py,sha256=g6PaU99GY5XvaRHx4BGmWHUpcJ36-Zh_GzeNVOeHviM,2856
16
16
  napistu/gcs/downloads.py,sha256=SvGv9WYr_Vt3guzyz1QiAuBndeKPTBtWSFLj1-QbLf4,6348
17
17
  napistu/gcs/utils.py,sha256=eLSsvewWJdCguyj2k0ozUGP5BTemaE1PZg41Z3aY5kM,571
18
18
  napistu/ingestion/__init__.py,sha256=dFXAhIqlTLJMwowS4BUDT08-Vy3Q0u1L0CMCErSZT1Y,239
19
- napistu/ingestion/bigg.py,sha256=q0HeVSO6pFftbrxxVfFGUtMvCoak9Wi9ngMggRfjFjo,4364
19
+ napistu/ingestion/bigg.py,sha256=f65--8ARe248eYCUJpFMF284Wz53sLyFyBuwelxHmJA,4340
20
20
  napistu/ingestion/constants.py,sha256=9UP47VImZ11q0kz17N3EJg2155USqLewwNWyKpA-cbA,8089
21
21
  napistu/ingestion/gtex.py,sha256=X0hSC1yrpf4xSJWFhpeNcnHwJzKDII2MvjfUqYA0JN8,3720
22
22
  napistu/ingestion/hpa.py,sha256=R27ExrryKQ4Crxv9ATXmBJCa-yd01TMOrDjkeBhIQac,5054
@@ -26,12 +26,12 @@ napistu/ingestion/obo.py,sha256=AQkIPWbjA464Lma0tx91JucWkIwLjC7Jgv5VHGRTDkE,9601
26
26
  napistu/ingestion/psi_mi.py,sha256=5eJjm7XWogL9oTyGqR52kntHClLwLsTePKqCvUGyi-w,10111
27
27
  napistu/ingestion/reactome.py,sha256=Hn9X-vDp4o_HK-OtaQvel3vJeZ8_TC1-4N2rruK9Oks,7099
28
28
  napistu/ingestion/sbml.py,sha256=N7neMwjTEF7OMhAcNvQJ29V_d3PqMLjLOZqvJTlK9q0,24743
29
- napistu/ingestion/string.py,sha256=YSWqaKm3I8bOixzvSA8fU4yfR2izddPYs4qJiqwjbxk,11678
29
+ napistu/ingestion/string.py,sha256=eLboW-dkaaiypwW6DxM3P1mGVweM3wqGsJPYKyImMAI,11713
30
30
  napistu/ingestion/trrust.py,sha256=ccjZc_eF3PdxxurnukiEo_e0-aKc_3z22NYbaJBtHdY,9774
31
- napistu/ingestion/yeast.py,sha256=bwFBNxRq-dLDaddgBL1hpfZj0eQ56nBXyR_9n0NZT9Y,5233
31
+ napistu/ingestion/yeast.py,sha256=7XwdkmgOnG1MYauKSk9nSK6fHemDrtXEPcS4ebs1_so,5268
32
32
  napistu/matching/__init__.py,sha256=dFXAhIqlTLJMwowS4BUDT08-Vy3Q0u1L0CMCErSZT1Y,239
33
33
  napistu/matching/constants.py,sha256=j4XSOE9Bpma9F6apVJ1LijKOUPgRk8Geo_u_rvNtpSU,610
34
- napistu/matching/interactions.py,sha256=5GQCOFVvPPS7tCJodcSy51Fzh4c-biaP1dbbhgc8VLs,18818
34
+ napistu/matching/interactions.py,sha256=XrzZvH1zgeaZLq3qhpsV0tx4BFgvNvkjM47l8ZUEOH4,18798
35
35
  napistu/matching/mount.py,sha256=8JEtiDIy7qdjWyDAs0vuVwEQkpwRf5ah4xMLZ4jKHag,19428
36
36
  napistu/matching/species.py,sha256=5-hmH8_UrvDmGfeJ1283trkCtPOC3BEmtYPcMHuZyNY,18893
37
37
  napistu/mcp/__init__.py,sha256=EmtcdtYyfhXdxxPB5cY_pshXnFv6XZ5CtRU0JMHn3aQ,2074
@@ -56,21 +56,21 @@ napistu/modify/constants.py,sha256=H6K6twzPlxt0yp6QLAxIx0Tp8YzYhtKKXPdmXi5V_QQ,3
56
56
  napistu/modify/curation.py,sha256=sQeSO53ZLdn14ww2GSKkoP0vJnDpAoSWb-YDjUf5hDQ,21743
57
57
  napistu/modify/gaps.py,sha256=qprylC2BbSk_vPWayYPVT8lwURXDMOlW5zNLV_wMFZ4,26755
58
58
  napistu/modify/pathwayannot.py,sha256=xuBSMDFWbg_d6-Gzv0Td3Q5nnFTa-Qzic48g1b1AZtQ,48081
59
- napistu/modify/uncompartmentalize.py,sha256=U5X4Q7Z-YIkC8_711x3sU21vTVdv9rKfauwz4JNzl6c,9690
59
+ napistu/modify/uncompartmentalize.py,sha256=y5LkXn5x6u80dB_McfAIh88BxZGIAVFLujkP7sPNRh0,9690
60
60
  napistu/network/__init__.py,sha256=dFXAhIqlTLJMwowS4BUDT08-Vy3Q0u1L0CMCErSZT1Y,239
61
61
  napistu/network/constants.py,sha256=fC1njZDu6in1JiaZ1-T1_fhmmkcc2HKSUUomDVyQ7Dw,5789
62
62
  napistu/network/data_handling.py,sha256=mxplWwyXNrjZRN-jjWWUI9IZOqX69k8qSMDIrL9h0Og,14736
63
- napistu/network/ig_utils.py,sha256=87vFuyLksYhfGoKBvjYeQt1yM_lg83m-14OaGbW64Wg,10478
63
+ napistu/network/ig_utils.py,sha256=JSlf_sZtw3DiiSIiYJ2YqJFEP4hVJMwNRox2qYTA4zY,11470
64
64
  napistu/network/napistu_graph_core.py,sha256=2NbjiLcDcFWFyX1MuN17pobPDgoQFtcYWOwuPSFTT4g,10429
65
65
  napistu/network/neighborhoods.py,sha256=Q9HWUvf_J4a_4RQDKd7ywEy4cp3Wq2OPOfVsotDbEe0,56098
66
- napistu/network/net_create.py,sha256=2N5ocGmibdBxIUVtv3H36iFWwkbys9ECCERFRlByhLc,69407
66
+ napistu/network/net_create.py,sha256=aAw6kfHREpkMEcwQFgwU5CHg--b8YLO559surQLRXZI,69408
67
67
  napistu/network/net_propagation.py,sha256=89ZR4p2mGpkCCIemofZ53XbUjQsuNABxIc6UmF8A5n8,4935
68
68
  napistu/network/ng_utils.py,sha256=ijWDa5MTuULJpdV6dcVFGmLmtB_xy87jaUG7F5nvC_k,15240
69
- napistu/network/paths.py,sha256=S4ZaV0yVmI-o8sXfom5eXA3yy2IEbleYUyXEvnmVw98,17468
70
- napistu/network/precompute.py,sha256=_TyztdHucczZg1JacWuXfNp5NGRKBFMGfp8Imx7OBMM,9118
69
+ napistu/network/paths.py,sha256=-dxRtaBRDYwuMw9DByGSn5OXFC3umDeO2zvVvD0TdWE,17452
70
+ napistu/network/precompute.py,sha256=pIXCCE6Mf6HY8o-fiwUaOxvQ_9_mevK0vaC8fND4RZk,9141
71
71
  napistu/ontologies/__init__.py,sha256=dFXAhIqlTLJMwowS4BUDT08-Vy3Q0u1L0CMCErSZT1Y,239
72
72
  napistu/ontologies/constants.py,sha256=GyOFvezSxDK1VigATcruTKtNhjcYaid1ggulEf_HEtQ,4345
73
- napistu/ontologies/dogma.py,sha256=jGZS-J3d29AoUOow-HVjfVZQJ87lnqO5L1aozieN1ec,8825
73
+ napistu/ontologies/dogma.py,sha256=VVj6NKBgNym4SdOSu8g22OohALj7cbObhIJmdY2Sfy0,8860
74
74
  napistu/ontologies/genodexito.py,sha256=5_gtdVtKHp5z-jtPXkHlZRiTsuHdZeA3TVI9u4SXqrQ,24725
75
75
  napistu/ontologies/mygene.py,sha256=RMFQTWsLkeYxmsOPxxmeIya2phdcUMcF5V2abaS8MVg,11109
76
76
  napistu/ontologies/renaming.py,sha256=bL15T0CswjM81NFVIVp-3CEsa1kxIIk2xJ2kE5eTeeg,6817
@@ -81,13 +81,13 @@ napistu/rpy2/rids.py,sha256=AfXLTfTdonfspgAHYO0Ph7jSUWv8YuyT8x3fyLfAqc8,3413
81
81
  napistu/scverse/__init__.py,sha256=Lgxr3iMQAkTzXE9BNz93CndNP5djzerLvmHM-D0PU3I,357
82
82
  napistu/scverse/constants.py,sha256=0iAkhyJUIeFGHdLLU3fCaEU1O3Oix4qAsxr3CxGTjVs,653
83
83
  napistu/scverse/loading.py,sha256=jqiE71XB-wdV50GyZrauFNY0Lai4bX9Fm2Gv80VR8t8,27016
84
- napistu-0.3.4.dist-info/licenses/LICENSE,sha256=kW8wVT__JWoHjl2BbbJDAZInWa9AxzJeR_uv6-i5x1g,1063
84
+ napistu-0.3.6.dist-info/licenses/LICENSE,sha256=kW8wVT__JWoHjl2BbbJDAZInWa9AxzJeR_uv6-i5x1g,1063
85
85
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
86
- tests/conftest.py,sha256=XVkd0tQywhnf2cgab7fIjBo3NlaTVX3cO8HaRS2jIwM,3190
87
- tests/test_consensus.py,sha256=3dJvvPsPG7bHbw_FY4Pm647N_Gt_Ud9157OKYfPCUd4,9502
86
+ tests/conftest.py,sha256=v8ZxJKqglEPlQ5ew3hM5eJQW2N0YhX9dOAaOQqdjyIc,4325
87
+ tests/test_consensus.py,sha256=qnf6Ntc3q7oCji1aisH9Ziy6KbnQCf1REizinR8ier8,12454
88
88
  tests/test_constants.py,sha256=gJLDv7QMeeBiiupyMazj6mumk20KWvGMgm2myHMKKfc,531
89
89
  tests/test_context_discretize.py,sha256=5Mr9WqwHGYMO37M1TnMmSfC64UZ73mnoCiEM2IQHVDY,1667
90
- tests/test_context_filtering.py,sha256=0CWbmxKxerraJJgUqXiYHSYCfmRyOFvFRMG1u6mLbwI,9483
90
+ tests/test_context_filtering.py,sha256=5dwC2d-99CpLdnzzTf2NvEzsRuwiIj-YU8NFqwtWp0g,9485
91
91
  tests/test_curation.py,sha256=-Q2J0D7qs9PGjHZX-rM4NxRLLdwxoapytSo_98q9ItY,3864
92
92
  tests/test_gaps.py,sha256=mGGeyx1vUnmEOF__bsqfCYq9Y8a1P-9mefqna4Qgc_k,4062
93
93
  tests/test_gcs.py,sha256=p_uQWuY2TcGj3zV3qFC-GXBqj4St8YENR_XRpQ6UH5g,570
@@ -102,22 +102,22 @@ tests/test_mcp_config.py,sha256=GTu9vywqAHTYkolywdYS_BEIW3gBzs4A4qcneMSPpRk,7007
102
102
  tests/test_mcp_documentation_utils.py,sha256=OW0N2N_2IOktbYTcCWhhWz4bANi8IB60l1q3DJi8Ra4,810
103
103
  tests/test_mcp_server.py,sha256=bP3PWVQsEfX6-lAgXKP32njdg__o65n2WuLvkxTTHkQ,11215
104
104
  tests/test_network_data_handling.py,sha256=oBSZuB3IRG9bwmD6n8FY-UZLe2UqGzXpNSxVtkHRSvE,12605
105
- tests/test_network_ig_utils.py,sha256=uojDLtL7oT9S9NJrXL8kBEHHFq5DB1GnJQT0v-gHEBE,632
105
+ tests/test_network_ig_utils.py,sha256=Buoh570mNm5pcac3Hf6f3pevCjWfBwPfKuD8IkDLg58,2120
106
106
  tests/test_network_neighborhoods.py,sha256=8BV17m5X1OUd5FwasTTYUOkNYUHDPUkxOKH_VZCsyBE,631
107
107
  tests/test_network_net_create.py,sha256=VNFZTwQawAZQPDnVk_qFevgZErx5KyQZ24bMoZF4T4w,16462
108
108
  tests/test_network_net_propagation.py,sha256=9pKkUdduWejH4iKNCJXKFzAkdNpCfrMbiUWySgI_LH4,3244
109
109
  tests/test_network_ng_utils.py,sha256=CwDw4MKTPhVZXz2HA2XU2QjjBv8CXc1_yQ0drvkBkFw,724
110
110
  tests/test_network_paths.py,sha256=TWZnxY5bF3m6gahcxcYJGrBIawh2-_vUcec1LyPmXV8,1686
111
111
  tests/test_network_precompute.py,sha256=xMGmZI9DxcWhJxuP7GCZEqtmcOvDRNK2LSia0x94v0U,9018
112
- tests/test_ontologies_genodexito.py,sha256=hBlunyEPiKskqagjWKW5Z6DJwKvpueYHJLwbfyeeAdo,2256
113
- tests/test_ontologies_mygene.py,sha256=BuBLm8VatzpK39-Ew_fFTK9ueLE4eqmKIDS5UKE59n8,1541
112
+ tests/test_ontologies_genodexito.py,sha256=6fINyUiubHZqu7qxye09DQfJXw28ZMAJc3clPb-cCoY,2298
113
+ tests/test_ontologies_mygene.py,sha256=VkdRcKIWmcG6V-2dpfvsBiOJN5dO-j0RqZNxtJRcyBU,1583
114
114
  tests/test_ontologies_renaming.py,sha256=k7bQzP24zG7W3fpULwk1me2sOWEWlxylr4Mhx1_gJJY,3740
115
115
  tests/test_pathwayannot.py,sha256=bceosccNy9tgxQei_7j7ATBSSvBSxOngJvK-mAzR_K0,3312
116
116
  tests/test_rpy2_callr.py,sha256=UVzXMvYN3wcc-ikDIjH2sA4BqkbwiNbMm561BcbnbD4,2936
117
117
  tests/test_rpy2_init.py,sha256=APrNt9GEQV9va3vU5k250TxFplAoWFc-FJRFhM2GcDk,5927
118
118
  tests/test_sbml.py,sha256=f25zj1NogYrmLluvBDboLameTuCiQ309433Qn3iPvhg,1483
119
- tests/test_sbml_dfs_core.py,sha256=tFaLMMuVjTLuhL-wimvcBbodEp59dhyHvXZ-IlUGGeU,19222
120
- tests/test_sbml_dfs_utils.py,sha256=5lNzZ1NLOnFb_sZ0YWTgLzXy28yGNCtS_H8Q-W-T6Bw,2022
119
+ tests/test_sbml_dfs_core.py,sha256=Gef-wf0AI7adwS_CBUokiJCw7yyGzInwAUnAj7IRE-E,20311
120
+ tests/test_sbml_dfs_utils.py,sha256=-EAW6N_elEOSQdsdRhnEdDhnZQH_weCGpnVOd2Xaepc,6963
121
121
  tests/test_sbo.py,sha256=x_PENFaXYsrZIzOZu9cj_Wrej7i7SNGxgBYYvcigLs0,308
122
122
  tests/test_scverse_loading.py,sha256=bnU1lQSYYWhOAs0IIBoi4ZohqPokDQJ0n_rtkAfEyMU,29948
123
123
  tests/test_set_coverage.py,sha256=J-6m6LuOjcQa9pxRuWglSfJk4Ltm7kt_eOrn_Q-7P6Q,1604
@@ -126,8 +126,8 @@ tests/test_uncompartmentalize.py,sha256=nAk5kfAVLU9a2VWe2x2HYVcKqj-EnwmwddERIPRa
126
126
  tests/test_utils.py,sha256=JRJFmjDNZpjG59a-73JkTyGqa_a7Z8d0fE2cZt0CRII,22580
127
127
  tests/utils.py,sha256=SoWQ_5roJteFGcMaOeEiQ5ucwq3Z2Fa3AAs9iXHTsJY,749
128
128
  tests/test_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
129
- napistu-0.3.4.dist-info/METADATA,sha256=u0M0PcjZJds7ds-cV2MYar8maae9_ld_9FO--0G3xwA,3414
130
- napistu-0.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
131
- napistu-0.3.4.dist-info/entry_points.txt,sha256=_QnaPOvJNA3IltxmZgWIiBoen-L1bPYX18YQfC7oJgQ,41
132
- napistu-0.3.4.dist-info/top_level.txt,sha256=Gpvk0a_PjrtqhYcQ9IDr3zR5LqpZ-uIHidQMIpjlvhY,14
133
- napistu-0.3.4.dist-info/RECORD,,
129
+ napistu-0.3.6.dist-info/METADATA,sha256=vJqqDnZ9MU1mmU_qTFgGnBgaYx4zt-yqg26H6VDkqz0,3414
130
+ napistu-0.3.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
131
+ napistu-0.3.6.dist-info/entry_points.txt,sha256=_QnaPOvJNA3IltxmZgWIiBoen-L1bPYX18YQfC7oJgQ,41
132
+ napistu-0.3.6.dist-info/top_level.txt,sha256=Gpvk0a_PjrtqhYcQ9IDr3zR5LqpZ-uIHidQMIpjlvhY,14
133
+ napistu-0.3.6.dist-info/RECORD,,
tests/conftest.py CHANGED
@@ -1,8 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import functools
3
4
  import os
4
5
  import sys
6
+ import threading
5
7
 
8
+ import pytest
6
9
 
7
10
  from napistu import consensus
8
11
  from napistu import indices
@@ -109,3 +112,43 @@ def pytest_runtest_setup(item):
109
112
  # Skip tests that should run only on Unix
110
113
  if not is_unix and any(mark.name == "unix_only" for mark in item.iter_markers()):
111
114
  skip("Test runs only on Unix systems")
115
+
116
+
117
+ def skip_on_timeout(timeout_seconds):
118
+ """Cross-platform decorator that skips a test if it takes longer than timeout_seconds"""
119
+
120
+ def decorator(func):
121
+ @functools.wraps(func)
122
+ def wrapper(*args, **kwargs):
123
+ result = [None]
124
+ exception = [None]
125
+ finished = [False]
126
+
127
+ def target():
128
+ try:
129
+ result[0] = func(*args, **kwargs)
130
+ finished[0] = True
131
+ except Exception as e:
132
+ exception[0] = e
133
+ finished[0] = True
134
+
135
+ thread = threading.Thread(target=target)
136
+ thread.daemon = True
137
+ thread.start()
138
+ thread.join(timeout_seconds)
139
+
140
+ if not finished[0]:
141
+ # Thread is still running, timeout occurred
142
+ pytest.skip(f"Test skipped due to timeout ({timeout_seconds}s)")
143
+
144
+ if exception[0]:
145
+ raise exception[0]
146
+
147
+ return result[0]
148
+
149
+ return wrapper
150
+
151
+ return decorator
152
+
153
+
154
+ pytest.skip_on_timeout = skip_on_timeout
tests/test_consensus.py CHANGED
@@ -7,8 +7,10 @@ import pytest
7
7
  from napistu import consensus
8
8
  from napistu import indices
9
9
  from napistu import source
10
+ from napistu import sbml_dfs_core
10
11
  from napistu.ingestion import sbml
11
12
  from napistu.modify import pathwayannot
13
+ from napistu.constants import SBML_DFS, SBML_DFS_SCHEMA
12
14
 
13
15
  test_path = os.path.abspath(os.path.join(__file__, os.pardir))
14
16
  test_data = os.path.join(test_path, "test_data")
@@ -246,6 +248,91 @@ def test_consensus_ontology_check():
246
248
  assert post_shared_onto_sp_set == {"chebi", "reactome", "uniprot"}
247
249
 
248
250
 
251
+ def test_report_consensus_merges_reactions(tmp_path):
252
+ # Create two minimal SBML_dfs objects with a single reaction each, same r_id
253
+ r_id = "R00000001"
254
+ reactions = pd.DataFrame(
255
+ {
256
+ SBML_DFS.R_NAME: ["rxn1"],
257
+ SBML_DFS.R_IDENTIFIERS: [None],
258
+ SBML_DFS.R_SOURCE: [None],
259
+ SBML_DFS.R_ISREVERSIBLE: [False],
260
+ },
261
+ index=[r_id],
262
+ )
263
+ reactions.index.name = SBML_DFS.R_ID
264
+ reaction_species = pd.DataFrame(
265
+ {
266
+ SBML_DFS.R_ID: [r_id],
267
+ SBML_DFS.SC_ID: ["SC0001"],
268
+ SBML_DFS.STOICHIOMETRY: [1],
269
+ SBML_DFS.SBO_TERM: ["SBO:0000459"],
270
+ },
271
+ index=["RSC0001"],
272
+ )
273
+ reaction_species.index.name = SBML_DFS.RSC_ID
274
+ compartmentalized_species = pd.DataFrame(
275
+ {
276
+ SBML_DFS.SC_NAME: ["A [cytosol]"],
277
+ SBML_DFS.S_ID: ["S0001"],
278
+ SBML_DFS.C_ID: ["C0001"],
279
+ SBML_DFS.SC_SOURCE: [None],
280
+ },
281
+ index=["SC0001"],
282
+ )
283
+ compartmentalized_species.index.name = SBML_DFS.SC_ID
284
+ species = pd.DataFrame(
285
+ {
286
+ SBML_DFS.S_NAME: ["A"],
287
+ SBML_DFS.S_IDENTIFIERS: [None],
288
+ SBML_DFS.S_SOURCE: [None],
289
+ },
290
+ index=["S0001"],
291
+ )
292
+ species.index.name = SBML_DFS.S_ID
293
+ compartments = pd.DataFrame(
294
+ {
295
+ SBML_DFS.C_NAME: ["cytosol"],
296
+ SBML_DFS.C_IDENTIFIERS: [None],
297
+ SBML_DFS.C_SOURCE: [None],
298
+ },
299
+ index=["C0001"],
300
+ )
301
+ compartments.index.name = SBML_DFS.C_ID
302
+ sbml_dict = {
303
+ SBML_DFS.COMPARTMENTS: compartments,
304
+ SBML_DFS.SPECIES: species,
305
+ SBML_DFS.COMPARTMENTALIZED_SPECIES: compartmentalized_species,
306
+ SBML_DFS.REACTIONS: reactions,
307
+ SBML_DFS.REACTION_SPECIES: reaction_species,
308
+ }
309
+ sbml1 = sbml_dfs_core.SBML_dfs(sbml_dict, validate=False, resolve=False)
310
+ sbml2 = sbml_dfs_core.SBML_dfs(sbml_dict, validate=False, resolve=False)
311
+ sbml_dfs_dict = {"mod1": sbml1, "mod2": sbml2}
312
+
313
+ # Create a lookup_table that merges both reactions into a new_id
314
+ lookup_table = pd.DataFrame(
315
+ {
316
+ "model": ["mod1", "mod2"],
317
+ "r_id": [r_id, r_id],
318
+ "new_id": ["merged_rid", "merged_rid"],
319
+ }
320
+ )
321
+ # Use the reactions schema
322
+ table_schema = SBML_DFS_SCHEMA.SCHEMA[SBML_DFS.REACTIONS]
323
+
324
+ # Call the function and check that it runs and the merge_labels are as expected
325
+ consensus.report_consensus_merges(
326
+ lookup_table.set_index(["model", "r_id"])[
327
+ "new_id"
328
+ ], # this is a Series with name 'new_id'
329
+ table_schema,
330
+ sbml_dfs_dict=sbml_dfs_dict,
331
+ n_example_merges=1,
332
+ )
333
+ # No assertion: this is a smoke test to ensure the Series output is handled without error
334
+
335
+
249
336
  ################################################
250
337
  # __main__
251
338
  ################################################
@@ -256,3 +343,4 @@ if __name__ == "__main__":
256
343
  test_source_tracking()
257
344
  test_passing_entity_data()
258
345
  test_consensus_ontology_check()
346
+ test_report_consensus_merges_reactions()
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import copy
4
4
  import pytest
5
5
  import pandas as pd
6
- from napistu import sbml_dfs_core
6
+ from napistu import sbml_dfs_utils
7
7
  from napistu.constants import SBML_DFS
8
8
  from napistu.context.filtering import (
9
9
  filter_species_by_attribute,
@@ -208,7 +208,7 @@ def test_filter_reactions_with_disconnected_cspecies(sbml_dfs):
208
208
  first_reactions = list(sbml_dfs.reactions.index[:5])
209
209
 
210
210
  # 2. Find defining species in these reactions
211
- reaction_species = sbml_dfs_core.add_sbo_role(sbml_dfs.reaction_species)
211
+ reaction_species = sbml_dfs_utils.add_sbo_role(sbml_dfs.reaction_species)
212
212
  defining_species = (
213
213
  reaction_species[reaction_species[SBML_DFS.R_ID].isin(first_reactions)]
214
214
  .query("sbo_role == 'DEFINING'")
@@ -6,6 +6,15 @@ from napistu.network import ig_utils
6
6
  from napistu.network import net_create
7
7
 
8
8
 
9
+ @pytest.fixture
10
+ def multi_component_graph() -> ig_utils.ig.Graph:
11
+ """Creates a graph with multiple disconnected components of different sizes."""
12
+ g1 = ig_utils.ig.Graph.Ring(5) # 5 vertices, 5 edges
13
+ g2 = ig_utils.ig.Graph.Tree(3, 2) # 3 vertices, 2 edges
14
+ g3 = ig_utils.ig.Graph.Full(2) # 2 vertices, 1 edge
15
+ return ig_utils.ig.disjoint_union([g1, g2, g3])
16
+
17
+
9
18
  def test_validate_graph_attributes(sbml_dfs):
10
19
 
11
20
  napistu_graph = net_create.process_napistu_graph(
@@ -21,3 +30,30 @@ def test_validate_graph_attributes(sbml_dfs):
21
30
  assert ig_utils.validate_vertex_attributes(napistu_graph, "node_type") is None
22
31
  with pytest.raises(ValueError):
23
32
  ig_utils.validate_vertex_attributes(napistu_graph, "baz")
33
+
34
+
35
+ def test_filter_to_largest_subgraph(multi_component_graph):
36
+ """Tests that the function returns only the single largest component."""
37
+ largest = ig_utils.filter_to_largest_subgraph(multi_component_graph)
38
+ assert isinstance(largest, ig_utils.ig.Graph)
39
+ assert largest.vcount() == 5
40
+ assert largest.ecount() == 5
41
+
42
+
43
+ def test_filter_to_largest_subgraphs(multi_component_graph):
44
+ """Tests that the function returns the top K largest components."""
45
+ # Test getting the top 2
46
+ top_2 = ig_utils.filter_to_largest_subgraphs(multi_component_graph, top_k=2)
47
+ assert isinstance(top_2, list)
48
+ assert len(top_2) == 2
49
+ assert all(isinstance(g, ig_utils.ig.Graph) for g in top_2)
50
+ assert [g.vcount() for g in top_2] == [5, 3]
51
+
52
+ # Test getting more than the total number of components
53
+ top_5 = ig_utils.filter_to_largest_subgraphs(multi_component_graph, top_k=5)
54
+ assert len(top_5) == 3
55
+ assert [g.vcount() for g in top_5] == [5, 3, 2]
56
+
57
+ # Test invalid top_k
58
+ with pytest.raises(ValueError):
59
+ ig_utils.filter_to_largest_subgraphs(multi_component_graph, top_k=0)
@@ -1,4 +1,6 @@
1
1
  import pandas as pd
2
+ import pytest
3
+
2
4
  from napistu.ontologies.genodexito import Genodexito
3
5
  from napistu.ontologies.constants import (
4
6
  GENODEXITO_DEFS,
@@ -7,6 +9,7 @@ from napistu.ontologies.constants import (
7
9
  )
8
10
 
9
11
 
12
+ @pytest.skip_on_timeout(5)
10
13
  def test_genodexito_mapping_operations():
11
14
  """Test Genodexito mapping table creation and operations."""
12
15
  # Initialize with test mode and Python method to avoid R dependencies
@@ -1,7 +1,10 @@
1
+ import pytest
2
+
1
3
  from napistu.ontologies.mygene import create_python_mapping_tables
2
4
  from napistu.ontologies.constants import INTERCONVERTIBLE_GENIC_ONTOLOGIES
3
5
 
4
6
 
7
+ @pytest.skip_on_timeout(5)
5
8
  def test_create_python_mapping_tables_yeast():
6
9
  """Test create_python_mapping_tables with yeast species."""
7
10
  # Test with a subset of mappings to keep test runtime reasonable