From 648c0cd1cdf09f547658427a46a6687898cc49f2 Mon Sep 17 00:00:00 2001 From: masikol Date: Wed, 4 Dec 2024 14:28:57 +0300 Subject: [PATCH] v3.3.b --- CHANGELOG.md | 36 +++++++++++---------- README.md | 2 +- con-hi.py | 20 ++++++++---- src/arguments.py | 45 +++++++++++++------------- src/dedupl_features.py | 2 +- src/dependencies.py | 27 ++++++++-------- src/main.py | 62 ++++++++++++++++-------------------- src/obtain_coverage.py | 11 +++---- src/output.py | 21 ++++++------ src/parse_fasta_reference.py | 10 +++--- src/printing.py | 7 ---- 11 files changed, 117 insertions(+), 126 deletions(-) delete mode 100644 src/printing.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 27940e1..ec331ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,11 @@ # 2024-12-04 edition -Version `3.3.a` +## Version `3.3.b` + +Improved progress reporting. + +## Version `3.3.a` 1. Added option `-r / --target-seq-ids`. One can use it to avoid wasting time annotating all sequences in a target fasta file if some of them are unwanted. Or to annotate sequences in a multi-fasta file separately (e.g. with different thresholds) without splitting the fasta file. @@ -12,13 +16,13 @@ Version `3.3.a` # 2023-12-18 edition -Version `3.2.a` +## Version `3.2.a` Rename “consensus-highlighter” to “con-hi” for the sake of simplicity. # 2023-11-01 edition -Version `3.1.b` +## Version `3.1.b` Now, when outputting sequence records, the program will just print emerging generic Biopython warning in a human-friendly way, without extra technical lines. @@ -39,7 +43,7 @@ What is more important, sole catching Biopython warnings will not now cause empt # 2023-10-29 edition -Version `3.1.a` +## Version `3.1.a` 1. Fix a bug that would cause the program to terminate if `-C` threshold is enabled and a high-coverage region starts at position 0 or ends at position (LENGTH-1) of the reference sequence. @@ -47,13 +51,13 @@ Version `3.1.a` ## 2023-05-25 edition -Version `3.0.b` +## Version `3.0.b` Fix a bug preventing `con-hi` from parsing `samtools version` output correctly if `samtools` is compiled with a flag `-ffile-prefix-map`. In that case, the `samtools version` output contains some non-utf8 characters. ## 2022-07-26 edition -Version `3.0.a` +## Version `3.0.a` 1. Add option `-l/min-feature-len`. It sets minimum length of an output feature. @@ -67,7 +71,7 @@ Version `3.0.a` ## 2022-04-26 edition -Version `2.3.a` +## Version `2.3.a` 1. Add recommendation "samtools `1.13` or later is recommended". This is the version, in which `samtools depth` [had beed](https://github.com/samtools/samtools/releases/tag/1.13) completely rewritten. Since 1.13, `samtools depth` calculates coverage more accurately. @@ -75,7 +79,7 @@ Version `2.3.a` ## 2022-02-23 edition -Version `2.2.b` +## Version `2.2.b` Changes: 1. Now con-hi removes its temporary file `coverages.tsv`, where coverage value of each base is stored. @@ -83,7 +87,7 @@ Changes: ## 2022-02-10 edition -Version `2.2.a` +## Version `2.2.a` Now con-hi adds a comment to output GenBank files. Here is the example of such a comment: @@ -99,19 +103,19 @@ COMMENT ##Coverage-Data-START## ## 2022-01-24 edition -Version `2.1.a` +## Version `2.1.a` Now con-hi is compatible with samtools 1.13+. ## 2021-12-23 edition -Version `2.0.b` +## Version `2.0.b` Now con-hi calculates and prints average coverage. ## 2021-12-09 edition -Version `2.0.a` +## Version `2.0.a` Removed options `-o/--outdir` and `--prefix`. @@ -119,13 +123,13 @@ Added option `-o/--outfile`. And now con-hi.py writes all GenBank output records ## 2021-06-15 edition -Version `1.1.a` +## Version `1.1.a` `con-hi` no more piles up coverage features with identical locations. It means that you will not see both "zero coverage" and "coverage < 10" features starting at the same positions and ending at the same positions. ## 2021-06-11 edition -Version `1.0.d` +## Version `1.0.d` Added warning messages for following cases: @@ -134,13 +138,13 @@ Added warning messages for following cases: ## 2021-06-10 edition -Version `1.0.b` +## Version `1.0.b` Fixed bug that would cause the program to stumble on a non-extant directory in PATH while checking dependencies. And then... -Version `1.0.c` +## Version `1.0.c` Fixed bug that cause the program to stumble on lowercase input sequences. diff --git a/README.md b/README.md index 05f28e9..cdc5336 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ “Con-hi” means “**con**sensus-**hi**ghlighter”. -Latest version is `3.3.a` (2024-12-04 edition). +Latest version is `3.3.b` (2024-12-04 edition). ## Description diff --git a/con-hi.py b/con-hi.py index 48e939b..ab11f69 100755 --- a/con-hi.py +++ b/con-hi.py @@ -2,20 +2,28 @@ import sys -__version__: str = '3.3.a' +__version__ = '3.3.b' # Year, month, day -__last_update_date__: str = '2024-12-04' -__min_python_version__: float = 3.6 +__last_update_date__ = '2024-12-04' +__min_python_version__ = 3.6 # __author__ = 'Maksim Sikolenko' +import logging +logging.basicConfig( + format='%(asctime)s: %(levelname)s -- %(message)s', + # datefmt='%Y-%m-%d %I:%M:%S %p', + level=logging.INFO +) + + # === Check python interpreter version === if sys.version_info.major + sys.version_info.minor*0.1 < __min_python_version__: - print( + logging.critical( 'Your python interpreter version is ' + '%d.%d' % (sys.version_info.major, sys.version_info.minor) ) - print(' Please, use Python %.1f+.\a' % __min_python_version__) + logging.critical(' Please, use Python %.1f+.\a' % __min_python_version__) # In python 2 'raw_input' does the same thing as 'input' in python 3. # Neither does 'input' in python2. if sys.platform.startswith('win'): @@ -51,7 +59,7 @@ # === Print name of the program and version === -print('\ncon-hi - Version {}'.format(__version__)) +print('\n== con-hi - Version {} ==\n'.format(__version__)) # === Check dependencies === diff --git a/src/arguments.py b/src/arguments.py index 56fbc2f..63bf518 100644 --- a/src/arguments.py +++ b/src/arguments.py @@ -3,9 +3,9 @@ import re import sys import getopt +import logging from typing import List, Sequence, Iterable, Set -from src.printing import print_err from src.platform import platf_depend_exit @@ -92,17 +92,17 @@ def parse_arguments() -> HighlighterArgs: ] ) except getopt.GetoptError as err: - print_err(str(err)) + logging.error(str(err)) platf_depend_exit(2) # end try # Check positional arguments: their existance is an error signal if len(args) != 0: - print_err('Error: con-hi.py does not take any positional arguments.') - print_err('You passed following positional argument(s):') + logging.error('Error: con-hi.py does not take any positional arguments.') + logging.error('You passed following positional argument(s):') arg: str for arg in args: - print_err(f' `{arg}`') + logging.error(f'`{arg}`') # end for platf_depend_exit(2) # end if @@ -112,12 +112,12 @@ def parse_arguments() -> HighlighterArgs: # Check mandatory options if args.target_fasta_fpath is None: - print_err('Error: option `-f` (`--target-fasta`) is mandatory.') + logging.error('Error: option `-f` (`--target-fasta`) is mandatory.') platf_depend_exit(2) # end if if args.bam_fpath is None: - print_err('Error: option `-b` (`--bam`) is mandatory.') + logging.error('Error: option `-b` (`--bam`) is mandatory.') platf_depend_exit(2) # end if @@ -152,14 +152,14 @@ def _parse_options(opts: List[List[str]]) -> HighlighterArgs: if opt in ('-f', '--target-fasta'): if not os.path.isfile(arg): - print_err(f'\aError: file {arg}` does not exist.') + logging.error(f'\aError: file {arg}` does not exist.') platf_depend_exit(2) # end if if not _is_fasta(arg): - print_err('\aError: only plain fasta or gzipped fasta are supported.') - print_err(f'Erroneous file: `{arg}`.') - print_err('Allowed extentions: `.fasta`, `.fa`, `.fasta.gz`, `.fa.gz`.') + logging.error('\aError: only plain fasta or gzipped fasta are supported.') + logging.error(f'Erroneous file: `{arg}`.') + logging.error('Allowed extentions: `.fasta`, `.fa`, `.fasta.gz`, `.fa.gz`.') platf_depend_exit(2) # end if @@ -167,20 +167,19 @@ def _parse_options(opts: List[List[str]]) -> HighlighterArgs: # Target reference sequence IDs elif opt in ('-r', '--target-seq-ids'): - # TODO: check if all seqids are in target fasta args.target_seq_ids = set(arg.split(_ARG_SEP)) # BAM file elif opt in ('-b', '--bam'): if not os.path.isfile(arg): - print_err(f'\aError: file {arg}` does not exist.') + logging.error(f'\aError: file {arg}` does not exist.') platf_depend_exit(2) # end if if not _is_bam(arg): - print_err(f'\aError: file `{arg}` does not seem like a BAM file.') - print_err('The program only accepts BAM files having `.bam` extentions') + logging.error(f'\aError: file `{arg}` does not seem like a BAM file.') + logging.error('The program only accepts BAM files having `.bam` extentions') platf_depend_exit(2) # end if @@ -201,11 +200,11 @@ def _parse_options(opts: List[List[str]]) -> HighlighterArgs: if any(map(_coverage_not_parsable, cov_strings)): invalid_strings: Iterable[str] = filter(_coverage_not_parsable, cov_strings) - print_err(f'\aError: invalid coverage thresholds in `{arg}`:') + logging.error(f'\aError: invalid coverage thresholds in `{arg}`:') for s in invalid_strings: - print_err(f' `{s}`') + logging.error(f' `{s}`') # end for - print_err('Coverage thesholds must be positive integer numbers.') + logging.error('Coverage thesholds must be positive integer numbers.') platf_depend_exit(2) # end if @@ -227,11 +226,11 @@ def _parse_options(opts: List[List[str]]) -> HighlighterArgs: if any(map(_coefficient_not_parsable, coef_strings)): invalid_strings: Iterable[str] = filter(_coefficient_not_parsable, coef_strings) - print_err(f'\aError: invalid coverage coefficient in `{arg}`:\n') + logging.error(f'\aError: invalid coverage coefficient in `{arg}`:') for s in invalid_strings: - print_err(f' `{s}`\n') + logging.error(f'`{s}`') # end for - print_err('Coverage coefficients must be positive numbers.\n') + logging.error('Coverage coefficients must be positive numbers.') platf_depend_exit(2) # end if @@ -254,8 +253,8 @@ def _parse_options(opts: List[List[str]]) -> HighlighterArgs: raise ValueError # end if except ValueError: - print_err(f'\aError: invalid minimum feature length: `{arg}`') - print_err('It must be non-negative negative number.') + logging.error(f'\aError: invalid minimum feature length: `{arg}`') + logging.error('It must be non-negative negative number.') platf_depend_exit(2) # end try args.min_feature_len = min_feature_len diff --git a/src/dedupl_features.py b/src/dedupl_features.py index 9690dd1..177ba4f 100644 --- a/src/dedupl_features.py +++ b/src/dedupl_features.py @@ -14,7 +14,7 @@ def dedupl_features( # :param new_features: list of recently discovered features; # :param extant_features: list of features that existed before `new_features` # was discovered; - + # Create the tuple of coordinates in order not to extract these locations each time extant_coords: MutableSequence[Tuple[int, int]] = tuple( ( diff --git a/src/dependencies.py b/src/dependencies.py index e8029c5..32d1ebb 100644 --- a/src/dependencies.py +++ b/src/dependencies.py @@ -1,12 +1,11 @@ import re import os -import sys +import logging import subprocess as sp from typing import List, Tuple, Sequence, Callable from src.platform import platf_depend_exit -from src.printing import print_err def check_depencencies() -> None: @@ -20,11 +19,11 @@ def check_depencencies() -> None: dependencies: Sequence[str] = ('Biopython', 'samtools') check_funcitons: Sequence[Callable[[], Tuple[str, str]]] = (_check_biopython, _check_samtools) - print('\nDependencies:') + logging.info('Dependencies:') for dep_name, chech_func in zip(dependencies, check_funcitons): - print(f'{dep_name}:', end='') + logging.info(f'{dep_name}:') version, err_msg = chech_func() # check the dependence # Append error message, if it exists @@ -32,19 +31,20 @@ def check_depencencies() -> None: err_msg_list.append(err_msg) # end if - print(f' version {version}') + logging.info(f'version {version}') # end for # Print errors, if they occured if len(err_msg_list) != 0: - print_err('Dependencies errors:') + logging.error('Dependencies errors:') for err_msg in err_msg_list: - print_err(f' - {err_msg}') + logging.error(f' - {err_msg}') # end for platf_depend_exit(1) # end if - print('All dependencies are satisfied.\n') + logging.info('All dependencies are satisfied.') + print('=' * 10) # end def @@ -134,16 +134,15 @@ def _check_samtools() -> Tuple[str, str]: def _check_recommended_samtools_version(full_version, numeric_version_str): recommended_version = 1.13 if float(numeric_version_str) < recommended_version: - print_err( - '\n - WARNING: your samtools version is {}, ' - 'although version {} is recommended.\n'.format( + logging.warning( + 'your samtools version is {}, ' + 'although version {} is recommended.'.format( full_version, recommended_version ) ) - print_err( - ' Versions older than {} may calculate coverage inaccurately.\n\n' \ + logging.warning( + ' Versions older than {} may calculate coverage inaccurately.' \ .format(recommended_version) ) - sys.stdout.flush() # end if # end def diff --git a/src/main.py b/src/main.py index b102b5f..55f877b 100644 --- a/src/main.py +++ b/src/main.py @@ -1,13 +1,11 @@ import os -import sys -import glob -from typing import Sequence, MutableSequence, List +import logging +from typing import Sequence, MutableSequence from Bio.SeqRecord import SeqRecord from Bio.SeqFeature import SeqFeature -from src.printing import print_err import src.output as out import src.obtain_coverage as oc import src.dedupl_features as ddf @@ -33,13 +31,12 @@ def main(version: str, last_update_date: str) -> None: with_warnings: str = '' # Read fasta records from input file - print('Importing fasta from `{}`...'.format(args.target_fasta_fpath), end=' ') - sys.stdout.flush() + logging.info('Parsing fasta from `{}`...'.format(args.target_fasta_fpath)) fasta_records: Sequence[SeqRecord] = pfr.parse_fasta_reference( args.target_fasta_fpath, args.target_seq_ids ) - print('done') + logging.info('Fasta parsing completed') # Create ouput directory _create_outdir_from_outfile(args.outfpath) @@ -49,47 +46,46 @@ def main(version: str, last_update_date: str) -> None: rec: SeqRecord for rec in fasta_records: - print(f'Processing sequence `{rec.description}`') + logging.info(f'Annotatiion started: `{rec.description}`') # Obtain path to coverage file coverage_fpath: str = out.conf_path_to_depth_file(args.outfpath, rec.id) # Count coverages with samtools depth - print('Silently counting coverages with `samtools depth`...', end=' ') - sys.stdout.flush() + logging.info('Silently counting coverages with `samtools depth`...') cov_fpath: str = oc.count_coverage( args.bam_fpath, rec.id, coverage_fpath ) - print('done\n') + logging.info('Coverage counting competed') # Obtain coverages for current sequence try: cov_array: CoverageArray = oc.get_coverage_for_reference(cov_fpath) except oc.MissingCoveragesError: - print_err('Warning: coverage values for this sequence are not found\n') + logging.warning('Coverage values for this sequence are not found') continue # end try # Check length of the coverage array if len(cov_array) == 0: - print_err(f'! Warning: no coverage information found for sequence `{rec.id}`.') - print_err(f"""! Please, make sure that field `RNAME` (3-rd column) in your BAM file contains -! id of this sequence specified in fasta header (i.e. `{rec.id}`).""") - print_err('! Omitting this sequence.') - print_err('=' * 10) + warning_str: str = f'No coverage information found for sequence `{rec.id}`. ' \ + 'Please, make sure that field `RNAME` (3-rd column) in your BAM file contains ' \ + f'id of this sequence specified in fasta header (i.e. `{rec.id}`).' \ + 'Omitting this sequence.' + logging.warning(warning_str) with_warnings = ' with warnings' continue # end if if len(cov_array) != len(rec.seq): - print_err(f"""! Warning: length of sequence `{rec.id}` ({len(rec.seq)} bp) -! is not equal to number of coverage positions ({len(cov_array)}) reported by `samtools depth` -! and stored in coverage file `{cov_fpath}`.""") - print_err('! Re-creating the bam file might be the solution of this issue.') - print_err('! Omitting this sequence.') - print_err('=' * 10) + warning_str: str = f'Length of sequence `{rec.id}` ({len(rec.seq)} bp) ' \ + f'is not equal to number of coverage positions ({len(cov_array)}) reported by `samtools depth` ' \ + f'and stored in coverage file `{cov_fpath}`. ' \ + 'Re-creating the bam file might be the solution of this issue. ' \ + 'Omitting this sequence.' + logging.warning(warning_str) with_warnings = ' with warnings' continue # end if @@ -102,8 +98,7 @@ def main(version: str, last_update_date: str) -> None: # Detect all necessary coverage features for cov_threshold in cov_thresholds: - sys.stdout.write(f'Screening the sequence for regions with {cov_threshold.get_label()}...') - sys.stdout.flush() + logging.info(f'Screening the sequence for regions with {cov_threshold.get_label()}...') # Get coverage features coverage_features = hlft.highlight_coverage_features( @@ -117,15 +112,14 @@ def main(version: str, last_update_date: str) -> None: # Append features to list rec.features.extend(coverage_features) - print(' done') + logging.info(f'{cov_threshold.get_label()} regions annotated') # end for if not args.keep_tmp_cov_file: _try_rm_temp_file(coverage_fpath) # end if - sys.stdout.write(f'Writing annotated sequence to `{args.outfpath}`...') - sys.stdout.flush() + logging.info('Saving annotated sequence...') # Write result GanBank record out.write_genbank_output( @@ -135,12 +129,12 @@ def main(version: str, last_update_date: str) -> None: cov_array, args.outfpath ) - print('done') + logging.info(f'Annotated sequence saved to `{args.outfpath}`') print('=' * 10) # end for - print(f'Completed{with_warnings}!') + logging.info(f'Completed{with_warnings}!') # end def @@ -155,8 +149,8 @@ def _create_outdir_from_outfile(outfpath: str) -> None: try: os.makedirs(outdpath) except OSError as err: - print_err(f'Error! Cannot create output directory `{outdpath}`.') - print_err(str(err)) + logging.error(f'Cannot create output directory `{outdpath}`.') + logging.error(str(err)) platf_depend_exit(1) # end try # end if @@ -167,8 +161,8 @@ def _try_rm_temp_file(file_path): try: os.unlink(file_path) except OSError as err: - print_err('Warning: cannot remove temporary file `{}`'.format(file_path)) - print_err(str(err)) + logging.warning('Cannot remove temporary file `{}`'.format(file_path)) + logging.warning(str(err)) # end try # end def diff --git a/src/obtain_coverage.py b/src/obtain_coverage.py index 53d6c8e..f90ee25 100644 --- a/src/obtain_coverage.py +++ b/src/obtain_coverage.py @@ -1,9 +1,8 @@ -import os +import logging import subprocess as sp from typing import Tuple, Iterator -from src.printing import print_err from src.platform import platf_depend_exit from src.coverage_array import CoverageArray @@ -11,9 +10,9 @@ def count_coverage(bam_fpath: str, target_seq_id: str, coverage_fpath: str) -> str: - # TODO: doc string # Function counts coverages for all sequences from input fasta file. # :param bam_fpath: path to bam file; + # :param target_seq_id: target sequence id to count coverage for; # :param coverage_fpath: path to coverage file; # Returns path to file that contains coverage: # first column -- sequence id, second column -- position, third column -- coverage. @@ -31,8 +30,8 @@ def count_coverage(bam_fpath: str, # Print error if it occures if pipe.returncode != 0: - print_err('\nError occured while running `samtools depth`') - print_err(stdout_stderr[1].decode('utf-8')) + logging.error('An error occured while running `samtools depth`') + logging.error(stdout_stderr[1].decode('utf-8')) platf_depend_exit(pipe.returncode) # end if @@ -76,9 +75,9 @@ def get_coverage_for_reference(coverage_fpath: str) -> CoverageArray: def _conf_samtools_depth_cmd(bam_fpath: str, target_seq_id: str, coverage_fpath: str) -> str: - # TODO: doc string # Function configures command to count coverages. # :param bam_fpath: path to bam file; + # :param target_seq_id: target sequence id to count coverage for; # :param coverage_fpath: path to coverage file; return ' '.join( diff --git a/src/output.py b/src/output.py index 3fc8073..076496f 100644 --- a/src/output.py +++ b/src/output.py @@ -1,6 +1,6 @@ import os -import sys +import logging import datetime import warnings @@ -8,7 +8,6 @@ from Bio import BiopythonWarning from Bio.SeqRecord import SeqRecord -from src.printing import print_err from src.platform import platf_depend_exit from src.coverage_array import CoverageArray @@ -19,8 +18,8 @@ def create_or_emply_file(file_path): pass # end with except OSError as err: - print_err(f'\nError: cannot create file {file_path}') - print_err(str(err)) + logging.error(f'Error: cannot create file {file_path}') + logging.error(str(err)) platf_depend_exit(1) # end try # end def @@ -38,7 +37,6 @@ def write_genbank_output(seq_record: SeqRecord, # :param outfpath: path to output file; annotations = _create_annotations( - seq_record, organism, topology, cov_array @@ -66,13 +64,13 @@ def _write_gb_warning_safe(seq_record: SeqRecord, outfpath: str, mode: str = 'a' # Catch and ignore BiopythonWarning warnings.filterwarnings('error') try: - records_written = _write_gb(seq_record, outfpath, mode='a') + records_written = _write_gb(seq_record, outfpath, mode=mode) except BiopythonWarning as w: - print_err('\n! Warning: {}\n'.format(str(w))) + logging.warning(str(w)) # We will write the record (again) only if it hasn't been already written if records_written == 0: warnings.filterwarnings('ignore') - _write_gb(seq_record, outfpath, mode='a') + _write_gb(seq_record, outfpath, mode=mode) # end if finally: warnings.resetwarnings() @@ -89,8 +87,7 @@ def _write_gb(seq_record: SeqRecord, outfpath: str, mode: str = 'a') -> int: # end def -def _create_annotations(seq_record: SeqRecord, - organism: str, +def _create_annotations(organism: str, topology: str, cov_array: CoverageArray) -> dict: annotations = { @@ -121,9 +118,9 @@ def _get_date() -> str: def conf_path_to_depth_file(outfpath: str, target_seq_id: str) -> str: - # TODO: doc string # Function configures path to coverage file. - # :param outdir: path to output directory; + # :param outfpath: path to output file; + # :param target_seq_id: target sequence id; return os.path.join( os.path.dirname(outfpath), f'coverage_{target_seq_id}.tsv' diff --git a/src/parse_fasta_reference.py b/src/parse_fasta_reference.py index efcccbc..6d37976 100644 --- a/src/parse_fasta_reference.py +++ b/src/parse_fasta_reference.py @@ -7,14 +7,12 @@ from Bio import SeqIO from Bio.SeqRecord import SeqRecord -from src.arguments import HighlighterArgs - def parse_fasta_reference(ref_fasta_fpath: str, ref_seq_ids: Set[str] = set()) -> Sequence[SeqRecord]: - # TODO: doc comment # Function parses fasta file and returns collection of fasta records stored in this file. # :param ref_fasta_fpath: path to target fasta file; + # :param ref_seq_ids: ids of target sequences to parse; # Choose open function for the file open_func: Callable[[str], TextIO] @@ -102,7 +100,6 @@ def _is_plain_text(fpath: str) -> bool: def _validate_fasta_records(fasta_records: Sequence[SeqRecord]) -> None: - # TODO: doc string # Function validates input fasta file. # :param fasta_records: list of fasta records to validate; @@ -133,8 +130,9 @@ def _validate_fasta_records(fasta_records: Sequence[SeqRecord]) -> None: def _validate_user_target_seq_ids(ref_seq_ids: Set[str], fasta_records: Sequence[SeqRecord]): - # TODO: doc string - # Check if all ref_seq_ids are in fasta_records + # The function checks if all ref_seq_ids are in fasta_records + # :param ref_seq_ids: target sequence ids; + # :param fasta_records: parsed fasta records; seq_ids_in_fasta = set( map(lambda sr: sr.id, fasta_records) ) diff --git a/src/printing.py b/src/printing.py deleted file mode 100644 index 25f4080..0000000 --- a/src/printing.py +++ /dev/null @@ -1,7 +0,0 @@ - -import sys - - -def print_err(message=''): - sys.stderr.write(message) -# end def