bscampp 1.0.6__py3-none-any.whl → 1.0.8__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.
bscampp/__init__.py CHANGED
@@ -12,7 +12,7 @@ import logging, os
12
12
  # not really needed for BSCAMPP but safe to update here
13
13
  os.sys.setrecursionlimit(1000000)
14
14
 
15
- __version__ = "1.0.6"
15
+ __version__ = "1.0.8"
16
16
  _INSTALL_PATH = __path__[0]
17
17
 
18
18
  # global variables to store all loggers
bscampp/configs.py CHANGED
@@ -4,6 +4,8 @@ try:
4
4
  except ImportError:
5
5
  from ConfigParser import configparser
6
6
  from argparse import ArgumentParser, Namespace
7
+ import subprocess
8
+
7
9
  from bscampp.init_configs import init_config_file
8
10
  from bscampp import get_logger, log_exception
9
11
  #from bscampp.utils import inferDataType
@@ -22,7 +24,6 @@ _LOG = get_logger(__name__)
22
24
  Configuration defined by users and by default values
23
25
  '''
24
26
  class Configs:
25
- global _root_dir
26
27
 
27
28
  # basic input paths
28
29
  info_path = None # info file for pplacer or EPA-ng
@@ -117,6 +118,24 @@ def _read_config_file(filename, cparser, opts,
117
118
  setattr(opts, section, section_name_space)
118
119
  return config_defaults
119
120
 
121
+ # validate a given binary file is executable
122
+ def validate_binary_executable(name, path):
123
+ # 1. make sure path exists
124
+ if not os.path.exists(path):
125
+ raise FileNotFoundError(f"{path} is not found!")
126
+
127
+ # 2. make sure path is executable with no IO error
128
+ try:
129
+ p = subprocess.Popen([path], text=True, stdout=subprocess.PIPE,
130
+ stderr=subprocess.PIPE)
131
+ _, __ = p.communicate()
132
+ except OSError:
133
+ raise AssertionError(' '.join([
134
+ f"{path} is not executable! Please do the followings:",
135
+ f"\n\n\t1. obtain a working executable for the software: {name}",
136
+ f"\n\t2. modify {main_config_path} to replace the old executable path\n",
137
+ ]))
138
+
120
139
  '''
121
140
  Build Config class
122
141
  '''
@@ -170,6 +189,8 @@ def buildConfigs(parser, cmdline_args, child_process=False, rerun=False):
170
189
 
171
190
  # sanity check for existence of base placement binary path
172
191
  if Configs.placement_method == 'epa-ng':
173
- assert os.path.exists(Configs.epang_path), 'epa-ng not detected!'
192
+ validate_binary_executable(Configs.placement_method, Configs.epang_path)
193
+ #assert os.path.exists(Configs.epang_path), 'epa-ng not detected!'
174
194
  elif Configs.placement_method == 'pplacer':
175
- assert os.path.exists(Configs.pplacer_path), 'pplacer not detected!'
195
+ validate_binary_executable(Configs.placement_method, Configs.pplacer_path)
196
+ #assert os.path.exists(Configs.pplacer_path), 'pplacer not detected!'
bscampp/functions.py CHANGED
@@ -11,6 +11,10 @@ import bscampp.utils as utils
11
11
 
12
12
  import concurrent.futures
13
13
 
14
+ # suppress userwarning when doing subtree suppress_unifurcations
15
+ import warnings
16
+ warnings.filterwarnings("ignore", category=UserWarning)
17
+
14
18
  _LOG = get_logger(__name__)
15
19
 
16
20
  ############################# helper functions ################################
@@ -29,6 +33,8 @@ def recompileBinariesFromDir(dir):
29
33
 
30
34
  if cmake_p.returncode != 0:
31
35
  _LOG.error("cmake failed!")
36
+ print("STDOUT:", cmake_stdout)
37
+ print("STDERR:", cmake_stderr)
32
38
  exit(cmake_p.returncode)
33
39
  else:
34
40
  _LOG.warning("cmake succeeded!")
@@ -59,14 +65,26 @@ def ensureBinaryExecutable(binpath):
59
65
  _LOG.warning(f"{binpath} does not exist!")
60
66
  b_recompile = True
61
67
  else:
62
- p = subprocess.Popen([binpath], stdout=subprocess.PIPE,
63
- stderr=subprocess.PIPE)
64
- stdout, stderr = p.communicate()
68
+ """
69
+ added @ 6.13.2025 by Chengze Shen
70
+ - try-catch OSError to indicate that the binary files
71
+ - are not executable on the current sytem and need to be
72
+ - recompiled
73
+ """
74
+ try:
75
+ p = subprocess.Popen([binpath], stdout=subprocess.PIPE,
76
+ stderr=subprocess.PIPE)
77
+ stdout, stderr = p.communicate()
78
+ returncode = p.returncode
79
+ except OSError as e:
80
+ # indicating we need to recompile: anything other than 255 or -1
81
+ returncode = 7
82
+
65
83
  # 255 or -1 indicates that the binaries work
66
- if p.returncode == 255 or p.returncode == -1:
84
+ if returncode == 255 or returncode == -1:
67
85
  pass
68
86
  else:
69
- _LOG.warning(f"{binpath} return code is {p.returncode}!")
87
+ _LOG.warning(f"{binpath} return code is {returncode}!")
70
88
  b_recompile = True
71
89
 
72
90
  if b_recompile:
@@ -405,7 +423,8 @@ def placeQueriesToSubtrees(tree, leaf_dict, new_subtree_dict, placed_query_list,
405
423
  aln, qaln, cmdline_args, workdir, qname_map, qname_map_rev,
406
424
  pool, lock, dry_run=False):
407
425
  t0 = time.perf_counter()
408
- _LOG.info('Performing placement on each subtree...')
426
+ _LOG.info("Performing placement on each subtree with {}...".format(
427
+ Configs.placement_method))
409
428
 
410
429
  if dry_run:
411
430
  return dict()
@@ -461,7 +480,7 @@ def placeQueriesToSubtrees(tree, leaf_dict, new_subtree_dict, placed_query_list,
461
480
  job = EPAngJob(path=Configs.epang_path,
462
481
  info_path=Configs.info_path, tree_path=tmp_tree,
463
482
  aln_path=tmp_aln, qaln_path=tmp_qaln,
464
- outdir=subtree_dir, num_cpus=Configs.num_cpus)
483
+ outdir=subtree_dir, num_cpus=Configs.cpus_per_job)
465
484
  jobs.append(job)
466
485
  ## for EPA-ng, ensure that outpath name is changed to the one we want
467
486
  #_outpath = job.run(logging=f'subtree_{final_subtree_count}')
@@ -481,7 +500,7 @@ def placeQueriesToSubtrees(tree, leaf_dict, new_subtree_dict, placed_query_list,
481
500
  job = PplacerTaxtasticJob(path=Configs.pplacer_path,
482
501
  refpkg_dir=refpkg_dir,
483
502
  #molecule=Configs.molecule, model=Configs.model,
484
- outpath=tmp_output, num_cpus=Configs.num_cpus,
503
+ outpath=tmp_output, num_cpus=Configs.cpus_per_job,
485
504
  qaln_path=tmp_qaln)
486
505
  #tmp_output = job.run(logging=f'subtree_{final_subtree_count}')
487
506
  jobs.append(job)
@@ -520,10 +539,22 @@ def placeQueriesToSubtrees(tree, leaf_dict, new_subtree_dict, placed_query_list,
520
539
  field_to_idx = {field: i for i, field in enumerate(fields)}
521
540
 
522
541
  for tmp_place in place_json["placements"]:
523
- # convert qname back using qname_map_rev
524
- qname = qname_map_rev[tmp_place[tgt][0]]
525
- tmp_place[tgt][0] = qname
526
- placed_query_list.append(qname)
542
+ # Fixed @ 7.7.2025 - Chengze Shen
543
+ # - pplacer actually can report multiple items
544
+ # - in the ["nm"] field.
545
+ for _idx in range(len(tmp_place[tgt])):
546
+ # convert qname back using qname_map_rev
547
+ tmp_name = tmp_place[tgt][_idx]
548
+
549
+ # >EPA-ng: tgt=="n" --> qname is string
550
+ if isinstance(tmp_name, str):
551
+ qname = qname_map_rev[tmp_name]
552
+ tmp_place[tgt][_idx] = qname
553
+ # >pplacer: tgt=="nm" --> qname is a list of two fields
554
+ elif isinstance(tmp_name, list):
555
+ qname = qname_map_rev[tmp_name[0]]
556
+ tmp_place[tgt][_idx][0] = qname
557
+ placed_query_list.append(qname)
527
558
 
528
559
  #placed_query_list.append(tmp_place[tgt][0])
529
560
  for i in range(len(tmp_place["p"])):
bscampp/init_configs.py CHANGED
@@ -71,11 +71,6 @@ def init_config_file(homepath, rerun=False, prioritize_user_software=True):
71
71
  cparser.set('basic', 'hamming_distance_dir',
72
72
  os.path.join(tools_dir, 'hamming_distance'))
73
73
 
74
- # macOS TODO: need to recompile the binaries
75
- if 'macos' in platform_name.lower():
76
- cparser.set('basic', 'hamming_distance_dir',
77
- os.path.join(tools_dir, 'macOS', 'hamming_distance'))
78
-
79
74
  # prioritize user's software
80
75
  if prioritize_user_software:
81
76
  print('Detecting existing software from user\'s environment...')
bscampp/pipeline.py CHANGED
@@ -180,8 +180,6 @@ def clean_temp_files():
180
180
  _LOG.info(f'- Removed {temp}')
181
181
 
182
182
  def parseArguments(dry_run=False, method="BSCAMPP"):
183
- global _root_dir, main_config_path
184
-
185
183
  default_outdir = f"{method.lower()}_output"
186
184
  default_outname = f"{method.lower()}_result"
187
185
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: bscampp
3
- Version: 1.0.6
3
+ Version: 1.0.8
4
4
  Summary: BSCAMPP and SCAMPP - Scalable Phylogenetic Placement Tools
5
5
  Author-email: Eleanor Wedell <ewedell2@illinois.edu>, Chengze Shen <chengze5@illinois.edu>
6
6
  License: MIT License
@@ -49,12 +49,13 @@ Requires-Dist: ConfigParser>=5.0.0
49
49
  Requires-Dist: numpy>=1.21.6
50
50
  Requires-Dist: treeswift>=1.1.45
51
51
  Requires-Dist: taxtastic>=0.9.3
52
+ Dynamic: license-file
52
53
 
53
54
  # BSCAMPP and SCAMPP - Two Scalable Phylogenetic Placement Methods and Frameworks
55
+ [![PyPI - Version](https://img.shields.io/pypi/v/bscampp)](https://pypi.org/project/bscampp/#history)
54
56
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/bscampp)](https://pypi.org/project/bscampp/)
55
- [![PyPI - Version](https://img.shields.io/pypi/v/bscampp?color=blue)](https://pypi.org/project/bscampp/#history)
56
57
  [![Build Status](https://img.shields.io/github/actions/workflow/status/ewedell/BSCAMPP/python-package.yml?branch=main&label=build)](https://github.com/ewedell/BSCAMPP/)
57
- [![PyPI - License](https://img.shields.io/pypi/l/bscampp?color=blue)](https://github.com/ewedell/BSCAMPP/blob/main/LICENSE)
58
+ [![PyPI - License](https://img.shields.io/pypi/l/bscampp)](https://github.com/ewedell/BSCAMPP/blob/main/LICENSE)
58
59
  [![Changelog](https://img.shields.io/badge/CHANGELOG-grey)](https://github.com/ewedell/BSCAMPP/blob/main/CHANGELOG.md)
59
60
 
60
61
  **Table of Contents**
@@ -105,8 +106,11 @@ please contact Eleanor Wedell (ewedell2@illinois.edu).
105
106
  EPA-ng and/or pplacer are requirements since BSCAMPP and SCAMPP will use them as the base phylogenetic placement methods.
106
107
  By default, the software will search for binary executables of `pplacer` and `epa-ng` in the user's environment when running for the first time.
107
108
  We also included a compiled version of `pplacer` for the Linux system under `bscampp/tools`.
108
- * **C++ OpenMP**:
109
- We also use OpenMP to speed up the similarity comparison between sequences using C++, which is required to run the pre-compiled binaries.
109
+ * **(macOS) EPA-ng and pplacer**:
110
+ For macOS systems, you need to have `EPA-ng` and/or `pplacer` installed in your environment.
111
+ * **cmake and C++ OpenMP**:
112
+ We also use C++ with OpenMP to speed up the similarity comparison between sequences, which is required to run the pre-compiled binaries.
113
+ In the case of macOS systems, the software will automatically recompile the binaries using `cmake`, which is required to be installed.
110
114
 
111
115
  ### (1) Install with `pip`
112
116
  The easiest way to install BSCAMPP and SCAMPP is to use `pip install`. This will also install all required Python packages.
@@ -1,10 +1,10 @@
1
- bscampp/__init__.py,sha256=eDIMYifzKrFdtA3Ac7OvPTyIHUO1ZLgVaM0pKFxxEHE,2289
2
- bscampp/configs.py,sha256=perl6u5hto6J3JV1JMbsTQ6tqr2uGOk-Z9jfzflid0s,6122
1
+ bscampp/__init__.py,sha256=IB6IZQO2RRGF0EOTQ5wL5zzwFXACvuINO9gD3KV4klA,2289
2
+ bscampp/configs.py,sha256=z40mkOwYTeqNjpcEDtyA4G3LskN86sZWc4Vdk46OywA,7014
3
3
  bscampp/default.config,sha256=CEfsUHBy--vwJhEcUuJ0btfuGQWb_lKMVWUIP9f5YGw,112
4
- bscampp/functions.py,sha256=DGHQJLLzXSghDKbha0LW0YPip_45M6MI4t3zdDpzULI,22448
5
- bscampp/init_configs.py,sha256=EA9sMN5jWj6zj2b-7tN19LhX2Ef61ByQLxQRLHAqLDM,3600
4
+ bscampp/functions.py,sha256=f5YY9LaIW3LieFvlWvcUlWysEuqVuwJXTmOq5laFxJE,23782
5
+ bscampp/init_configs.py,sha256=hDCH2oatgli2wSmi-uDM96TSiBvLKTAhobBSSKfjl2g,3381
6
6
  bscampp/jobs.py,sha256=v7buZJs1AnNoXiILwu-W8fo3QjxAh3i9Mp7xfmlJvAY,7569
7
- bscampp/pipeline.py,sha256=IPZnXZmVxGGfbVUuGCQh5X9oBq48-6pA9QkuvMGPTag,14000
7
+ bscampp/pipeline.py,sha256=J-RQH54R27m6fhzIpGX0MJuE3ZFk5rcnsROpwC_n5CE,13960
8
8
  bscampp/utils.py,sha256=-wns6FaWMKD2wVqjxdBQvjTdagTjywBIaGfqb2mupe4,30039
9
9
  bscampp/tools/epa-ng,sha256=f3EVoZAAOXLN6l521qp-TrWDl5J2nqL3tGgjPaQE9WQ,3772096
10
10
  bscampp/tools/pplacer,sha256=p0H4eo9uuiYoWS_kJbPfauOV99i7BXJdZSiwXIuLxTw,7834576
@@ -17,9 +17,9 @@ bscampp/tools/hamming_distance/src/fragment_tree_hamming.cpp,sha256=xCmyAT-OZJOD
17
17
  bscampp/tools/hamming_distance/src/fragment_tree_hamming_new.cpp,sha256=eKxgODRlpf0hU84QjNhigvRhWCT9tiJZjA5oQFQ1bUk,7404
18
18
  bscampp/tools/hamming_distance/src/homology.cpp,sha256=ZE0uXZWQ-cN4U1Wk5kUr_KKHgzsgA6Sno-IViRa4tmI,6053
19
19
  bscampp/tools/hamming_distance/src/new_hamming.cpp,sha256=fBRm99RquBZgZjaLOn9xDI3cH9NchhrxKbL-11j8fmk,5342
20
- bscampp-1.0.6.dist-info/LICENSE,sha256=HEa4YQdOR0e2Gz-NiOwr9X6aJcZtY0AGmlJQDmfN0Iw,1064
21
- bscampp-1.0.6.dist-info/METADATA,sha256=0sWAKK30wlps8i0d1BdFqyv5MZVgefRnTn_-yMmO8lQ,12602
22
- bscampp-1.0.6.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
23
- bscampp-1.0.6.dist-info/entry_points.txt,sha256=4Ft83qHc39tNNpMLgSgFXDHM-vuAB99JtmczCQj5pq8,204
24
- bscampp-1.0.6.dist-info/top_level.txt,sha256=1loGRUAft6Tcdq0f3lHbVwWN7W_SW1srfhAVSpg9DWE,8
25
- bscampp-1.0.6.dist-info/RECORD,,
20
+ bscampp-1.0.8.dist-info/licenses/LICENSE,sha256=HEa4YQdOR0e2Gz-NiOwr9X6aJcZtY0AGmlJQDmfN0Iw,1064
21
+ bscampp-1.0.8.dist-info/METADATA,sha256=2w_otUXBGIuvf6nXV-7hY958cxllhTZd6T18rB5nR8Q,12877
22
+ bscampp-1.0.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
+ bscampp-1.0.8.dist-info/entry_points.txt,sha256=4Ft83qHc39tNNpMLgSgFXDHM-vuAB99JtmczCQj5pq8,204
24
+ bscampp-1.0.8.dist-info/top_level.txt,sha256=1loGRUAft6Tcdq0f3lHbVwWN7W_SW1srfhAVSpg9DWE,8
25
+ bscampp-1.0.8.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (76.0.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5