diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f1763e6..cedf9831 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: Python CI on: push: - branches: [ master, main, dev-master, dev-refactored, refactor-phylo ] + branches: [ main, dev] pull_request: - branches: [ master, main, dev-master, dev-refactored, refactor-phylo ] + branches: [ main, dev] jobs: build: @@ -12,7 +12,7 @@ jobs: timeout-minutes: 10 strategy: matrix: - python-version: ["3.9", "3.10", "3.11"] + python-version: ["3.11", "3.12"] steps: - uses: actions/checkout@v3 @@ -56,4 +56,4 @@ jobs: - name: Notify failure if: failure() - run: echo "Build failed" \ No newline at end of file + run: echo "Build failed" diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 4afcdb63..00933679 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -28,7 +28,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.11' - name: Cache pip uses: actions/cache@v4 diff --git a/OrthoEvol/Cookies/cookie_jar.py b/OrthoEvol/Cookies/cookie_jar.py index 2d54ec21..399a9621 100644 --- a/OrthoEvol/Cookies/cookie_jar.py +++ b/OrthoEvol/Cookies/cookie_jar.py @@ -7,18 +7,17 @@ import yaml from cookiecutter.hooks import run_script from cookiecutter.main import cookiecutter -from pkg_resources import resource_filename - # OrthoEvol from OrthoEvol import Cookies from OrthoEvol.Manager.config import yml +from OrthoEvol.resources import package_resource_path from OrthoEvol.Tools.logit import LogIt class CookBook(object): """Class of cookiecutter related functions.""" - _config_file = resource_filename(yml.__name__, "cookie_recipes.yml") + _config_file = package_resource_path(yml, "cookie_recipes.yml") def __init__(self, config_file=_config_file, **new_recipes): """Access paths to the various cookiecutter templates. @@ -28,7 +27,7 @@ def __init__(self, config_file=_config_file, **new_recipes): New Recipes can also be added... """ - self.CookieJar = Path(resource_filename(Cookies.__name__, '')) + self.CookieJar = package_resource_path(Cookies) self.repo_cookie = self.CookieJar / Path('new_repository') self.user_cookie = self.CookieJar / Path('new_user') self.project_cookie = self.CookieJar / Path('new_project') diff --git a/OrthoEvol/Cookies/new_website/{{cookiecutter.website_name}}/requirements/dev.txt b/OrthoEvol/Cookies/new_website/{{cookiecutter.website_name}}/requirements/dev.txt index 29f280a9..6a820bf0 100644 --- a/OrthoEvol/Cookies/new_website/{{cookiecutter.website_name}}/requirements/dev.txt +++ b/OrthoEvol/Cookies/new_website/{{cookiecutter.website_name}}/requirements/dev.txt @@ -2,7 +2,7 @@ -r prod.txt # Testing -pytest==3.0.7 +pytest==9.1.1 WebTest==2.0.27 factory-boy==2.8.1 diff --git a/OrthoEvol/Cookies/new_website/{{cookiecutter.website_name}}/requirements/prod.txt b/OrthoEvol/Cookies/new_website/{{cookiecutter.website_name}}/requirements/prod.txt index be9f2e99..af50b550 100644 --- a/OrthoEvol/Cookies/new_website/{{cookiecutter.website_name}}/requirements/prod.txt +++ b/OrthoEvol/Cookies/new_website/{{cookiecutter.website_name}}/requirements/prod.txt @@ -1,43 +1,40 @@ # Everything needed in production # Flask -Flask==2.2.5 -MarkupSafe==1.0 +Flask==3.1.3 +MarkupSafe==3.0.3 Werkzeug==3.1.5 Jinja2==3.1.6 -itsdangerous==0.24 -click>=5.0 +itsdangerous==2.2.0 +click==8.4.2 # Database -Flask-SQLAlchemy==2.2 -psycopg2==2.7.1 -SQLAlchemy==1.2.19 +Flask-SQLAlchemy==3.1.1 +psycopg2-binary==2.9.12 +SQLAlchemy==2.0.51 # Migrations -Flask-Migrate==2.0.3 +Flask-Migrate==4.1.0 # Forms -Flask-WTF==0.14.2 -WTForms==2.1 +Flask-WTF==1.3.0 +WTForms==3.2.2 # Deployment gunicorn>=19.1.1 # Assets -Flask-Assets==0.12 +Flask-Assets==2.1.0 cssmin>=0.2.0 jsmin>=2.0.11 -# Admin -Flask-User==0.6.11 - # Auth -Flask-Login==0.4.0 -Flask-Bcrypt==0.7.1 +Flask-Login==0.6.3 +Flask-Bcrypt==1.0.1 # Caching Flask-Caching>=1.0.0 # Debug toolbar -Flask-DebugToolbar==0.10.1 +Flask-DebugToolbar==0.16.0 diff --git a/OrthoEvol/Manager/biosql/biosql.py b/OrthoEvol/Manager/biosql/biosql.py index 01a3e5de..4ccd3e90 100644 --- a/OrthoEvol/Manager/biosql/biosql.py +++ b/OrthoEvol/Manager/biosql/biosql.py @@ -1,7 +1,6 @@ # Standard Library import os import subprocess as sp -import pkg_resources from pathlib import Path # BioPython from BioSQL import BioSeqDatabase @@ -12,6 +11,7 @@ from OrthoEvol.Manager.management import ProjectManagement from OrthoEvol.Manager.biosql.biosql_repo import sql from OrthoEvol.Manager.biosql.biosql_repo import scripts as sql_scripts +from OrthoEvol.resources import package_resource_path class BaseBioSQL(object): @@ -46,9 +46,13 @@ def __init__(self, database_name, template_name="", project=None, project_path=N self.biosql_proc = self.biosql_utils.system_cmd # Load relative and absolute paths to scripts in the BioSQL module - self.scripts = pkg_resources.resource_filename(sql_scripts.__name__, "") - self.ncbi_taxon_script = pkg_resources.resource_filename(sql_scripts.__name__, "load_ncbi_taxonomy.pl") - self.itis_taxon_script = pkg_resources.resource_filename(sql_scripts.__name__, "load_itis_taxonomy.pl") + self.scripts = package_resource_path(sql_scripts) + self.ncbi_taxon_script = package_resource_path( + sql_scripts, "load_ncbi_taxonomy.pl" + ) + self.itis_taxon_script = package_resource_path( + sql_scripts, "load_itis_taxonomy.pl" + ) if database_name: self.database_name = Path(database_name) @@ -132,7 +136,7 @@ def load_sqlite_schema(self): """ # Build the command if not self.template_abs_path.is_file(): - schema_file = pkg_resources.resource_filename(sql.__name__, self.schema_file) + schema_file = package_resource_path(sql, self.schema_file) schema_cmd = self.schema_cmd % str(self.template_abs_path) # Run the bash command self.configure_new_database(schema_cmd, schema_file) diff --git a/OrthoEvol/Manager/config/templates/upload_rr_pbs.py b/OrthoEvol/Manager/config/templates/upload_rr_pbs.py index ec1aa6c1..7fbe6a30 100644 --- a/OrthoEvol/Manager/config/templates/upload_rr_pbs.py +++ b/OrthoEvol/Manager/config/templates/upload_rr_pbs.py @@ -58,7 +58,6 @@ def _dispatch_config(config_file): "from OrthoEvol.Manager.management import ProjectManagement\n" \ "from OrthoEvol.Manager.database_dispatcher import DatabaseDispatcher\n" \ "from OrthoEvol.Manager.config import yml\n" \ - "from pkg_resources import resource_filename\n" \ "import yaml\n" \ "pm_config_file = \"%%s\"\n" \ "with open(pm_config_file, \'r\') as f:\n" \ @@ -87,4 +86,4 @@ def _dispatch_config(config_file): # Dispatch PBS jobs disp, conf = _dispatch_config(config_file) -dd.dispatch(strategies=list(disp.keys()),dispatcher=disp, configuration=conf) \ No newline at end of file +dd.dispatch(strategies=list(disp.keys()),dispatcher=disp, configuration=conf) diff --git a/OrthoEvol/Manager/data_management.py b/OrthoEvol/Manager/data_management.py index 5843f984..15748ef5 100644 --- a/OrthoEvol/Manager/data_management.py +++ b/OrthoEvol/Manager/data_management.py @@ -1,14 +1,14 @@ # Standard Library -import pkg_resources -import yaml +import yaml # OrthoEvol -from OrthoEvol.Manager.management import ProjectManagement -from OrthoEvol.Manager import config +from OrthoEvol.Manager.management import ProjectManagement +from OrthoEvol.Manager.config import yml from OrthoEvol.Manager.database_management import BaseDatabaseManagement from OrthoEvol.Orthologs.Align import MultipleSequenceAlignment as MSA from OrthoEvol.Orthologs.Blast.blast import OrthoBlastN from OrthoEvol.Orthologs.Blast.comparative_genetics import BaseComparativeGenetics -from OrthoEvol.Orthologs.GenBank.genbank import GenBank +from OrthoEvol.Orthologs.GenBank.genbank import GenBank +from OrthoEvol.resources import package_resource_path #import configparser @@ -43,13 +43,13 @@ def __init__(self, config_file=None, pipeline=None, new=False, start=False, **kw self.GenBank_config = self.Alignment_config = None # Alignment configuration self.Guidance_config = self.Clustalo_config = self.Pal2Nal_config = None - self.pm = self.bl = self.gb = self.al = self.db = None - if pipeline == 'Ortho_CDS_1': - if new is True: - config_file = pkg_resources.resource_filename(config.yaml.__name__, 'pipeline.yml') - else: - config_file = pkg_resources.resource_filename( - config.yaml.__name__, 'config_template_existing.yml') + self.pm = self.bl = self.gb = self.al = self.db = None + if pipeline == 'Ortho_CDS_1': + if new is True: + config_file = package_resource_path(yml, 'pipeline.yml') + else: + config_file = package_resource_path( + yml, 'initialize_old.yml') if config_file is not None: if start is True: self.configure(config_file) diff --git a/OrthoEvol/Manager/database_management.py b/OrthoEvol/Manager/database_management.py index 4fde1107..236f2d76 100644 --- a/OrthoEvol/Manager/database_management.py +++ b/OrthoEvol/Manager/database_management.py @@ -8,7 +8,6 @@ from collections import OrderedDict from importlib import import_module from pathlib import Path -from pkg_resources import resource_filename import subprocess as sp # OrthoEvol from OrthoEvol import OrthoEvolDeprecationWarning @@ -19,6 +18,7 @@ from OrthoEvol.Manager.management import ProjectManagement from OrthoEvol.Orthologs.Blast.comparative_genetics import BaseComparativeGenetics from OrthoEvol.Manager.config import templates +from OrthoEvol.resources import package_resource_path class BaseDatabaseManagement(object): @@ -752,7 +752,7 @@ def NCBI_refseq_release(self, configure_flag=None, archive_flag=None, delete_fla db_path = self.database_path / Path('NCBI') / Path('refseq') / Path('release') / Path(collection_subset) # Read the upload script - upload_script = resource_filename(templates.__name__, 'upload_rr_pbs.py') + upload_script = package_resource_path(templates, 'upload_rr_pbs.py') with open(upload_script, 'r') as upload_script: temp_script = upload_script.read() rand_str = random.sample(string.ascii_letters + string.digits, 5) diff --git a/OrthoEvol/Manager/management.py b/OrthoEvol/Manager/management.py index 8c0edd61..9051a732 100644 --- a/OrthoEvol/Manager/management.py +++ b/OrthoEvol/Manager/management.py @@ -1,12 +1,12 @@ """Directory management tools for the package.""" # Standard Library import os -from pathlib import Path -import pkg_resources -# OrthoEvol -from OrthoEvol import Cookies, Orthologs, Manager, Tools -from OrthoEvol.Cookies import Oven -from OrthoEvol.Tools.logit import LogIt +from pathlib import Path +# OrthoEvol +from OrthoEvol import Cookies, Orthologs, Manager, Tools +from OrthoEvol.Cookies import Oven +from OrthoEvol.resources import package_resource_path +from OrthoEvol.Tools.logit import LogIt class Management(object): @@ -37,19 +37,19 @@ def __init__(self, repo=None, home=os.getcwd(), new_repo=False, **kwargs): self.Kitchen = Oven(repo=self.repo, output_dir=self.file_home) self.Pantry = self.Kitchen.Recipes # Manager Module: - self.Manager = Path(pkg_resources.resource_filename(Manager.__name__, '')) + self.Manager = package_resource_path(Manager) self.BioSQL = self.Manager / Path('BioSQL') self.SQLite3 = self.BioSQL / Path('sqlite') self.MySQL = self.BioSQL / Path('mysql') self.config = self.Manager / Path('config') # Orthologs Module: - self.Orthologs = Path(pkg_resources.resource_filename(Orthologs.__name__, '')) + self.Orthologs = package_resource_path(Orthologs) self.Align = self.Orthologs / Path('Align') self.Blast = self.Orthologs / Path('Blast') self.GenBank = self.Orthologs / Path('GenBank') self.Phylogenetics = self.Orthologs / Path('Phylogenetics') # Tools Module: - self.Tools = Path(pkg_resources.resource_filename(Tools.__name__, '')) + self.Tools = package_resource_path(Tools) self.ftp = self.Tools / Path('ftp') self.logit = self.Tools / Path('logit') self.mpi = self.Tools / Path('mpi') diff --git a/OrthoEvol/Manager/webster.py b/OrthoEvol/Manager/webster.py index ed88b6a3..47f209cb 100644 --- a/OrthoEvol/Manager/webster.py +++ b/OrthoEvol/Manager/webster.py @@ -1,10 +1,10 @@ # Standard Library import re import textwrap as twrap -from pathlib import Path -from pkg_resources import resource_filename -# OrthoEvol -from OrthoEvol.Manager.config import references +from pathlib import Path +# OrthoEvol +from OrthoEvol.Manager.config import references +from OrthoEvol.resources import package_resource_path @@ -39,16 +39,16 @@ class Webster(object): Unreliable Alignment Regions Accounting for the Uncertainty of Multiple Parameters. Nucleic Acids Research, 2015 Jul 1; 43 (Web Server issue): W7-W14.; doi: 10.1093/nar/gkq443""", "link": "https://www.ncbi.nlm.nih.gov/pubmed/18229673", - "path": resource_filename( - references.__name__, "GUIDANCE2_Accurate_Detection_of_Unreliable_Alignment_Regions_Accounting_for_" + "path": package_resource_path( + references, "GUIDANCE2_Accurate_Detection_of_Unreliable_Alignment_Regions_Accounting_for_" "the_Uncertainty_of_Multiple_Parameters.pdf") }, "reference_2": { "citation": u"""Landan, G., and D. Graur. (2008). Local Reliability Measures from Sets of Co-optimal Multiple Sequence Alignments. Pac Symp Biocomput 13:15-24""", "link": "https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4489236/", - "path": resource_filename( - references.__name__, "Local_Reliability_Measures_from_Sets_of_Co-optimal_Multiple_Sequence_" + "path": package_resource_path( + references, "Local_Reliability_Measures_from_Sets_of_Co-optimal_Multiple_Sequence_" "Alignments.pdf") } }, diff --git a/OrthoEvol/Orthologs/Blast/comparative_genetics.py b/OrthoEvol/Orthologs/Blast/comparative_genetics.py index 0b25ae47..b4ab8a92 100644 --- a/OrthoEvol/Orthologs/Blast/comparative_genetics.py +++ b/OrthoEvol/Orthologs/Blast/comparative_genetics.py @@ -5,13 +5,13 @@ import time import copy import random -import pkg_resources from pathlib import Path # OrthoEvol from OrthoEvol.Manager.config import data from OrthoEvol.Manager.management import ProjectManagement from OrthoEvol.utilities import FullUtilities from OrthoEvol.Tools.logit import LogIt +from OrthoEvol.resources import package_resource_path # Other import pandas as pd from ete3 import NCBITaxa @@ -125,7 +125,7 @@ def __init__(self, project=None, project_path=os.getcwd(), acc_file=None, self.taxon_path = self.project_index / Path(self.taxon_file) # Handle the master accession file (could be before or after blast) if kwargs["copy_from_package"]: - shutil.copy(pkg_resources.resource_filename(data.__name__, self.acc_file), + shutil.copy(package_resource_path(data, self.acc_file), str(self.project_index)) else: shutil.copy(self.acc_file, str(self.project_index)) diff --git a/OrthoEvol/Orthologs/Phylogenetics/PAML/codeml.py b/OrthoEvol/Orthologs/Phylogenetics/PAML/codeml.py index 3bfaa8df..ca78ca87 100644 --- a/OrthoEvol/Orthologs/Phylogenetics/PAML/codeml.py +++ b/OrthoEvol/Orthologs/Phylogenetics/PAML/codeml.py @@ -2,11 +2,11 @@ import os from pathlib import Path from shutil import copy -import pkg_resources -# BioPython +# BioPython from Bio.Phylo.PAML import codeml # OrthoEvol -from OrthoEvol.Manager.config import paml_control_files +from OrthoEvol.Manager.config import paml_control_files +from OrthoEvol.resources import package_resource_path class CodemlRun(object): @@ -39,8 +39,8 @@ def __init__(self, P2N_alignment, iqtree_newick, control_file='codeml-8-11-2017. # Set up genes control file name and get the OrthoEvol control file path self.gene = str(iqtree_newick).replace('_iqtree.nwk', '') self.control_file = self.paml_path / Path(self.gene + '.ctl') - self.control_template = pkg_resources.resource_filename( - paml_control_files.__name__, control_file) + self.control_template = package_resource_path( + paml_control_files, control_file) print(self.control_template) # Set up CODEML input files diff --git a/OrthoEvol/README.md b/OrthoEvol/README.md index c4779b34..c519ebb5 100644 --- a/OrthoEvol/README.md +++ b/OrthoEvol/README.md @@ -1,6 +1,6 @@ Tutorial ============= -OrthoEvolution has been built with Python 3.9 (and up) as a multi-faceted package and pipeline +OrthoEvolution has been built with Python 3.11 (and up) as a multi-faceted package and pipeline framework for comparative genetics in order to infer orthologous genes. Currently, this python package is comprised of 5 major modules: diff --git a/OrthoEvol/Tools/pbs/qstat.py b/OrthoEvol/Tools/pbs/qstat.py index 61ddac7b..1257a100 100644 --- a/OrthoEvol/Tools/pbs/qstat.py +++ b/OrthoEvol/Tools/pbs/qstat.py @@ -14,9 +14,9 @@ import plotly.graph_objs as go import yaml from dateutil import parser -from pkg_resources import resource_filename from OrthoEvol.Manager.config import yml +from OrthoEvol.resources import package_resource_path from OrthoEvol.Tools.logit import LogIt from OrthoEvol.utilities import FullUtilities @@ -82,7 +82,7 @@ def __init__(self, job_id, infile=None, outfile=None, home=None, cmd=None, captu self.qstat_utils = FullUtilities() self.qstat_log = LogIt().default(logname="PBS - QSTAT", logfile=None) - self._yaml_config = resource_filename(yml.__name__, 'qstat.yml') + self._yaml_config = package_resource_path(yml, 'qstat.yml') self.pbs_job_id = job_id self.capture_json = capture_json if not cmd: diff --git a/OrthoEvol/Tools/pbs/qsub.py b/OrthoEvol/Tools/pbs/qsub.py index c3c99fa4..28e5f380 100644 --- a/OrthoEvol/Tools/pbs/qsub.py +++ b/OrthoEvol/Tools/pbs/qsub.py @@ -9,9 +9,8 @@ from pathlib import Path from time import sleep -from pkg_resources import resource_filename - from OrthoEvol.Manager.config import templates +from OrthoEvol.resources import package_resource_path from OrthoEvol.Tools.logit import LogIt from OrthoEvol.utilities import FullUtilities @@ -184,7 +183,7 @@ def __init__(self, python_script=None, author=None, project_name="OrthoEvol", de else: self.supplied_python_script = self.python_script - self.pbs_template = resource_filename(templates.__name__, "temp.pbs") + self.pbs_template = package_resource_path(templates, "temp.pbs") # PBS - header info if not author: diff --git a/OrthoEvol/Tools/sge/sgejob.py b/OrthoEvol/Tools/sge/sgejob.py index 17d31ecf..ad2bd60d 100644 --- a/OrthoEvol/Tools/sge/sgejob.py +++ b/OrthoEvol/Tools/sge/sgejob.py @@ -1,13 +1,13 @@ from subprocess import run, CalledProcessError, PIPE import os from time import sleep -from pkg_resources import resource_filename from OrthoEvol.Tools.logit import LogIt from OrthoEvol.Tools.sge import (basejobids, writecodefile, import_temp, file2str) from OrthoEvol.Tools.sge.sgeconfig import __DEFAULT__ from OrthoEvol.Manager.config import templates +from OrthoEvol.resources import package_resource_path from OrthoEvol.Tools.sge import Qstat @@ -24,8 +24,8 @@ def __init__(self, base_jobname, config=None): self.sgejob_log = LogIt().default(logname="SGE JOB", logfile=None) self.pbsworkdir = os.getcwd() - # Import the temp.pbs file using pkg_resources - self.temp_pbs = resource_filename(templates.__name__, "temp.pbs") + # Resolve the bundled template once so jobs use a stable local path. + self.temp_pbs = package_resource_path(templates, "temp.pbs") @classmethod def _configure(cls, length, base_jobname): diff --git a/OrthoEvol/resources.py b/OrthoEvol/resources.py new file mode 100644 index 00000000..bdbb2ea6 --- /dev/null +++ b/OrthoEvol/resources.py @@ -0,0 +1,21 @@ +"""Helpers for accessing files bundled with OrthoEvol packages.""" + +from importlib.resources import files +from pathlib import Path +from types import ModuleType + + +def package_resource_path( + package: ModuleType, + resource_name: str = "", +) -> Path: + """Return a filesystem path for a resource in an installed package. + + OrthoEvol is configured as not zip-safe, so its installed resources are + real files and can be passed to tools that require filesystem paths. + """ + package_files = files(package) + resource = ( + package_files.joinpath(resource_name) if resource_name else package_files + ) + return Path(str(resource)) diff --git a/OrthoEvol/utilities.py b/OrthoEvol/utilities.py index 714fe810..b0f8d4ec 100644 --- a/OrthoEvol/utilities.py +++ b/OrthoEvol/utilities.py @@ -7,10 +7,10 @@ import sqlite3 import subprocess as sp import sys -import pkg_resources from threading import Timer from datetime import datetime from importlib import import_module +from importlib.metadata import version from pathlib import Path from subprocess import TimeoutExpired from tempfile import TemporaryFile @@ -895,9 +895,13 @@ def _getversion(self): :rtype: str """ import_module(self.packagename) - version = pkg_resources.get_distribution(self.packagename).version - utils_log.info('Version %s of %s is installed.' % (version, self.packagename)) - return version + package_version = version(self.packagename) + utils_log.info( + 'Version %s of %s is installed.' % ( + package_version, self.packagename + ) + ) + return package_version class FunctionRepeater(object): diff --git a/README.md b/README.md index 4be18877..0ae6a59f 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ to gain more insight into this project/python package. ## Installation -View the below methods for installing this package. Python 3.9 or higher +View the below methods for installing this package. Python 3.11 or higher is required. ### PyPI diff --git a/docs/docs/source/index.rst b/docs/docs/source/index.rst index f39aa758..f759f393 100644 --- a/docs/docs/source/index.rst +++ b/docs/docs/source/index.rst @@ -38,7 +38,7 @@ more insight into this project/python package. Installation ---------------- -View the below methods for installing this package. **Python 3.9 or higher is required.** +View the below methods for installing this package. **Python 3.11 or higher is required.** PyPi ~~~~~~~~~~~~~~~~ @@ -116,4 +116,4 @@ Indices & Tables ================== * :ref:`genindex` * :ref:`modindex` -* :ref:`search` \ No newline at end of file +* :ref:`search` diff --git a/docs/docs/source/tutorial/orthoevolreadme.rst b/docs/docs/source/tutorial/orthoevolreadme.rst index 11e8ba8b..12b20887 100644 --- a/docs/docs/source/tutorial/orthoevolreadme.rst +++ b/docs/docs/source/tutorial/orthoevolreadme.rst @@ -1,7 +1,7 @@ Tutorial ======== -OrthoEvolution has been built with Python 3.9 (and up) as a +OrthoEvolution has been built with Python 3.11 (and up) as a multi-faceted package and pipeline framework for comparative genetics in order to infer orthologous genes. diff --git a/pyproject.toml b/pyproject.toml index d1ad3783..889e9ed8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "OrthoEvol" version = "1.0.0b2" description = "This package aids in the analysis of orthologous genes." readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.11,<3.13" license = "MIT" license-files = ["LICENSE"] keywords = [ @@ -33,9 +33,8 @@ classifiers = [ "Operating System :: Unix", "Natural Language :: English", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Framework :: Flask" ] @@ -53,7 +52,7 @@ dependencies = [ "tablib>=0.11.5", "mygene>=3.0.0", "cookiecutter", - "Flask", + "Flask>=3.1.3,<4", "Flask-Login", "Flask-Mail", "Flask-SQLAlchemy", @@ -70,7 +69,7 @@ dependencies = [ [project.optional-dependencies] test = [ - "pytest", + "pytest>=9.1.1,<10", "pytest-cov", "pytest-runner" ] diff --git a/requirements.txt b/requirements.txt index b96add58..762a166f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ biopython>=1.79,<1.84 tablib>=0.11.5 mygene>=3.0.0 cookiecutter -Flask +Flask>=3.1.3,<4 Flask-Login Flask-Mail Flask-SQLAlchemy diff --git a/setup.py b/setup.py index 592c23fc..3000f101 100644 --- a/setup.py +++ b/setup.py @@ -8,27 +8,25 @@ https://github.com/biopython/biopython/blob/master/setup.py http://python-packaging.readthedocs.io/en/latest/index.html """ +from importlib import import_module +from pathlib import Path + from setuptools import setup from setuptools.command.install import install -import os -from importlib import import_module -import pkg_resources class PostInstallCommand(install): """Post-installation command to set BioSQL Perl script permissions.""" - def run(self): + def run(self) -> None: """Run the installation and then set script permissions.""" install.run(self) # Set up the permissions for the BioSQL Perl scripts try: scripts = import_module("OrthoEvol.Manager.biosql.biosql_repo.scripts") - biosql_scripts = pkg_resources.resource_filename(scripts.__name__, "") - for file in os.listdir(biosql_scripts): - if '.pl' in file: - script_path = os.path.join(biosql_scripts, file) - os.chmod(script_path, mode=755) + biosql_scripts = Path(next(iter(scripts.__path__))) + for script_path in biosql_scripts.glob("*.pl"): + script_path.chmod(0o755) except (ImportError, ModuleNotFoundError, FileNotFoundError): # Skip if module not available or files don't exist pass diff --git a/tests/test_manager.py b/tests/test_manager.py index 032815bf..7f1b0c9c 100644 --- a/tests/test_manager.py +++ b/tests/test_manager.py @@ -111,25 +111,12 @@ def test_management_with_repo(self): class TestRepoManagement(unittest.TestCase): """Test the RepoManagement class.""" - def setUp(self): + def setUp(self) -> None: """Set up test fixtures.""" self.test_dir = Path(tempfile.mkdtemp()) + self.addCleanup(rmtree, self.test_dir, ignore_errors=True) self.repo = 'test-repo' - def tearDown(self): - """Clean up test directories.""" - repo_path = self.test_dir / self.repo - if repo_path.exists(): - try: - rmtree(repo_path) - except (OSError, PermissionError): - pass - if self.test_dir.exists(): - try: - rmtree(self.test_dir) - except (OSError, PermissionError): - pass - def test_repo_management_init(self): """Test RepoManagement initialization.""" repo_mgmt = RepoManagement( @@ -160,26 +147,13 @@ def test_repo_management_with_user(self): class TestUserManagement(unittest.TestCase): """Test the UserManagement class.""" - def setUp(self): + def setUp(self) -> None: """Set up test fixtures.""" self.test_dir = Path(tempfile.mkdtemp()) + self.addCleanup(rmtree, self.test_dir, ignore_errors=True) self.repo = 'test-repo' self.user = 'test-user' - def tearDown(self): - """Clean up test directories.""" - repo_path = self.test_dir / self.repo - if repo_path.exists(): - try: - rmtree(repo_path) - except (OSError, PermissionError): - pass - if self.test_dir.exists(): - try: - rmtree(self.test_dir) - except (OSError, PermissionError): - pass - def test_user_management_init(self): """Test UserManagement initialization.""" # UserManagement requires project to be set (even if None) for Oven initialization @@ -216,26 +190,13 @@ def test_user_management_with_project(self): class TestWebsiteManagement(unittest.TestCase): """Test the WebsiteManagement class.""" - def setUp(self): + def setUp(self) -> None: """Set up test fixtures.""" self.test_dir = Path(tempfile.mkdtemp()) + self.addCleanup(rmtree, self.test_dir, ignore_errors=True) self.repo = 'test-repo' self.website = 'test-website' - def tearDown(self): - """Clean up test directories.""" - repo_path = self.test_dir / self.repo - if repo_path.exists(): - try: - rmtree(repo_path) - except (OSError, PermissionError): - pass - if self.test_dir.exists(): - try: - rmtree(self.test_dir) - except (OSError, PermissionError): - pass - def test_website_management_init(self): """Test WebsiteManagement initialization.""" web_mgmt = WebsiteManagement( diff --git a/tests/test_orthologs.py b/tests/test_orthologs.py index a154f9f0..47ee95bd 100644 --- a/tests/test_orthologs.py +++ b/tests/test_orthologs.py @@ -325,299 +325,62 @@ def tearDown(self): if os.path.exists(self.test_dir): rmtree(self.test_dir, ignore_errors=True) - def test_name_fasta_file_cds_single(self): - """Test name_fasta_file for CDS single mode.""" + def test_name_fasta_file(self) -> None: + """Test filename construction across feature types and write modes.""" gene = "HTR1A" org = "Homo_sapiens" - feat_type = "CDS" - feat_type_rank = "CDS" - extension = ".ffn" - mode = "w" - - file_obj = GenBank.name_fasta_file( - self.test_path, gene, org, feat_type, feat_type_rank, extension, mode - ) - - self.assertIsNotNone(file_obj) - self.assertEqual(file_obj.mode, 'w') - expected_path = self.test_path / f"{gene}_{org}{feat_type_rank}{extension}" - self.assertEqual(Path(file_obj.name), expected_path) - file_obj.close() - - def test_name_fasta_file_cds_multi(self): - """Test name_fasta_file for CDS multi mode.""" - gene = "HTR1A" - org = "Homo_sapiens" - feat_type = "CDS" - feat_type_rank = "CDS" - extension = ".ffn" - mode = "a" - - file_obj = GenBank.name_fasta_file( - self.test_path, gene, org, feat_type, feat_type_rank, extension, mode - ) - - self.assertIsNotNone(file_obj) - self.assertEqual(file_obj.mode, 'a') - expected_path = self.test_path / f"{gene}{feat_type_rank}{extension}" - self.assertEqual(Path(file_obj.name), expected_path) - file_obj.close() - - def test_name_fasta_file_other_single(self): - """Test name_fasta_file for other feature type single mode.""" - gene = "HTR1A" - org = "Homo_sapiens" - feat_type = "misc_feature" - feat_type_rank = "misc_feature" - extension = ".fna" - mode = "w" - - file_obj = GenBank.name_fasta_file( - self.test_path, gene, org, feat_type, feat_type_rank, extension, mode - ) - - self.assertIsNotNone(file_obj) - self.assertEqual(file_obj.mode, 'w') - expected_path = self.test_path / f"{gene}_{org}_{feat_type_rank}{extension}" - self.assertEqual(Path(file_obj.name), expected_path) - file_obj.close() - - def test_name_fasta_file_other_multi(self): - """Test name_fasta_file for other feature type multi mode.""" - gene = "HTR1A" - org = "Homo_sapiens" - feat_type = "misc_feature" - feat_type_rank = "misc_feature" - extension = ".fna" - mode = "a" - - file_obj = GenBank.name_fasta_file( - self.test_path, gene, org, feat_type, feat_type_rank, extension, mode - ) - - self.assertIsNotNone(file_obj) - self.assertEqual(file_obj.mode, 'a') - expected_path = self.test_path / f"{gene}_{feat_type_rank}{extension}" - self.assertEqual(Path(file_obj.name), expected_path) - file_obj.close() - - def test_protein_gi_fetch_with_gi(self): - """Test protein_gi_fetch when GI is present.""" - mock_feature = mock.Mock() - mock_feature.qualifiers = ['protein_id=NP_000000.1', 'GI:123456789', 'product=test protein'] - - result = GenBank.protein_gi_fetch(mock_feature) - - self.assertEqual(result, '123456789') - - def test_protein_gi_fetch_without_gi(self): - """Test protein_gi_fetch when GI is not present.""" - mock_feature = mock.Mock() - mock_feature.qualifiers = ['protein_id=NP_000000.1', 'product=test protein'] - - result = GenBank.protein_gi_fetch(mock_feature) - - self.assertIsNone(result) - - def test_protein_gi_fetch_empty_qualifiers(self): - """Test protein_gi_fetch with empty qualifiers.""" - mock_feature = mock.Mock() - mock_feature.qualifiers = [] - - result = GenBank.protein_gi_fetch(mock_feature) - - self.assertIsNone(result) - - @mock.patch('OrthoEvol.Orthologs.GenBank.genbank.FullUtilities') - @mock.patch('OrthoEvol.Orthologs.GenBank.genbank.LogIt') - @mock.patch('OrthoEvol.Orthologs.GenBank.genbank.Path.mkdir') - @mock.patch('os.listdir') - def test_genbank_init(self, mock_listdir, mock_mkdir, mock_logit, mock_utils): - """Test GenBank initialization.""" - mock_listdir.return_value = ['test.db', 'other.db', 'not_a_db.txt'] - mock_logit_instance = mock.Mock() - mock_logit.return_value.default.return_value = mock_logit_instance - - # Create a mock object with the required attributes - mock_config_obj = mock.Mock() - mock_config_obj.user_db = Path(self.test_dir) - mock_config_obj.ncbi_db_repo = Path(self.test_dir) - mock_config_obj.raw_data = Path(self.test_dir) - - # Set up the FullUtilities mock - mock_utils_instance = mock.Mock() - mock_utils_instance.attribute_config.return_value = mock_config_obj - mock_utils.return_value = mock_utils_instance - - genbank = GenBank( - project="test-project", - project_path=self.test_dir, - solo=True, - multi=True - ) - - self.assertIsNotNone(genbank) - self.assertEqual(genbank.project, "test-project") - self.assertEqual(genbank.project_path, self.test_dir) - self.assertTrue(genbank.solo) - self.assertTrue(genbank.multi) - self.assertTrue(genbank.min_fasta) - self.assertEqual(len(genbank.db_files_list), 2) - self.assertIn('test.db', genbank.db_files_list) - self.assertIn('other.db', genbank.db_files_list) - - -class TestAlign(unittest.TestCase): - """Test the Align module.""" - - def setUp(self): - """Set up test fixtures.""" - self.test_dir = tempfile.mkdtemp() - self.test_fasta = os.path.join(self.test_dir, 'test.fasta') - self.test_outfile = os.path.join(self.test_dir, 'test_aligned.fasta') - - # Create a simple test fasta file - with open(self.test_fasta, 'w') as f: - f.write('>seq1\nATGCATGC\n>seq2\nATGCATGC\n') - - def tearDown(self): - """Clean up test fixtures.""" - if os.path.exists(self.test_dir): - rmtree(self.test_dir, ignore_errors=True) - - def test_clustalo_init(self): - """Test ClustalO initialization.""" - clustalo = ClustalO( - infile=self.test_fasta, - outfile=self.test_outfile, - logpath=None, - outfmt="fasta" - ) - - self.assertIsNotNone(clustalo) - self.assertEqual(clustalo.infile, self.test_fasta) - self.assertEqual(clustalo.outfile, self.test_outfile) - self.assertEqual(clustalo.outfmt, "fasta") - self.assertIsNone(clustalo.logpath) - self.assertTrue(hasattr(clustalo, 'clustalolog')) - - @mock.patch('OrthoEvol.Orthologs.Align.msa.FullUtilities') - @mock.patch('OrthoEvol.Orthologs.Align.msa.LogIt') - def test_msa_init(self, mock_logit, mock_utils): - """Test MultipleSequenceAlignment initialization.""" - mock_logit_instance = mock.Mock() - mock_logit.return_value.default.return_value = mock_logit_instance - - # Create a mock object with the required attributes - mock_config_obj = mock.Mock() - mock_config_obj.raw_data = Path(self.test_dir) - - # Set up the FullUtilities mock - mock_utils_instance = mock.Mock() - mock_utils_instance.attribute_config.return_value = mock_config_obj - mock_utils.return_value = mock_utils_instance - - msa = MultipleSequenceAlignment( - project="test-project", - project_path=self.test_dir - ) - - self.assertIsNotNone(msa) - self.assertEqual(msa.project, "test-project") - self.assertTrue(hasattr(msa, 'guidancelog')) - self.assertTrue(hasattr(msa, 'pal2nallog')) - self.assertTrue(hasattr(msa, 'clustalolog')) - self.assertIsInstance(msa.dispatcher_options, dict) - - -class TestGenBank(unittest.TestCase): - """Test the GenBank class.""" - - def setUp(self): - """Set up test fixtures.""" - self.test_dir = tempfile.mkdtemp() - self.test_path = Path(self.test_dir) - - def tearDown(self): - """Clean up test fixtures.""" - if os.path.exists(self.test_dir): - rmtree(self.test_dir, ignore_errors=True) - - def test_name_fasta_file_cds_single(self): - """Test name_fasta_file for CDS single mode.""" - gene = "HTR1A" - org = "Homo_sapiens" - feat_type = "CDS" - feat_type_rank = "CDS" - extension = ".ffn" - mode = "w" - - file_obj = GenBank.name_fasta_file( - self.test_path, gene, org, feat_type, feat_type_rank, extension, mode - ) - - self.assertIsNotNone(file_obj) - self.assertEqual(file_obj.mode, 'w') - expected_path = self.test_path / f"{gene}_{org}{feat_type_rank}{extension}" - self.assertEqual(Path(file_obj.name), expected_path) - file_obj.close() - - def test_name_fasta_file_cds_multi(self): - """Test name_fasta_file for CDS multi mode.""" - gene = "HTR1A" - org = "Homo_sapiens" - feat_type = "CDS" - feat_type_rank = "CDS" - extension = ".ffn" - mode = "a" - - file_obj = GenBank.name_fasta_file( - self.test_path, gene, org, feat_type, feat_type_rank, extension, mode - ) - - self.assertIsNotNone(file_obj) - self.assertEqual(file_obj.mode, 'a') - expected_path = self.test_path / f"{gene}{feat_type_rank}{extension}" - self.assertEqual(Path(file_obj.name), expected_path) - file_obj.close() - - def test_name_fasta_file_other_single(self): - """Test name_fasta_file for other feature type single mode.""" - gene = "HTR1A" - org = "Homo_sapiens" - feat_type = "misc_feature" - feat_type_rank = "misc_feature" - extension = ".fna" - mode = "w" - - file_obj = GenBank.name_fasta_file( - self.test_path, gene, org, feat_type, feat_type_rank, extension, mode - ) - - self.assertIsNotNone(file_obj) - self.assertEqual(file_obj.mode, 'w') - expected_path = self.test_path / f"{gene}_{org}_{feat_type_rank}{extension}" - self.assertEqual(Path(file_obj.name), expected_path) - file_obj.close() - - def test_name_fasta_file_other_multi(self): - """Test name_fasta_file for other feature type multi mode.""" - gene = "HTR1A" - org = "Homo_sapiens" - feat_type = "misc_feature" - feat_type_rank = "misc_feature" - extension = ".fna" - mode = "a" - - file_obj = GenBank.name_fasta_file( - self.test_path, gene, org, feat_type, feat_type_rank, extension, mode + test_cases = ( + { + "name": "CDS single", + "feature_type": "CDS", + "feature_rank": "CDS", + "extension": ".ffn", + "mode": "w", + "expected_filename": "HTR1A_Homo_sapiensCDS.ffn", + }, + { + "name": "CDS multi", + "feature_type": "CDS", + "feature_rank": "CDS", + "extension": ".ffn", + "mode": "a", + "expected_filename": "HTR1ACDS.ffn", + }, + { + "name": "other feature single", + "feature_type": "misc_feature", + "feature_rank": "misc_feature", + "extension": ".fna", + "mode": "w", + "expected_filename": "HTR1A_Homo_sapiens_misc_feature.fna", + }, + { + "name": "other feature multi", + "feature_type": "misc_feature", + "feature_rank": "misc_feature", + "extension": ".fna", + "mode": "a", + "expected_filename": "HTR1A_misc_feature.fna", + }, ) - self.assertIsNotNone(file_obj) - self.assertEqual(file_obj.mode, 'a') - expected_path = self.test_path / f"{gene}_{feat_type_rank}{extension}" - self.assertEqual(Path(file_obj.name), expected_path) - file_obj.close() + for test_case in test_cases: + with self.subTest(test_case["name"]): + file_obj = GenBank.name_fasta_file( + self.test_path, + gene, + org, + test_case["feature_type"], + test_case["feature_rank"], + test_case["extension"], + test_case["mode"], + ) + + self.assertIsNotNone(file_obj) + self.assertEqual(file_obj.mode, test_case["mode"]) + expected_path = self.test_path / test_case["expected_filename"] + self.assertEqual(Path(file_obj.name), expected_path) + file_obj.close() def test_protein_gi_fetch_with_gi(self): """Test protein_gi_fetch when GI is present.""" diff --git a/tests/test_resource_consumers.py b/tests/test_resource_consumers.py new file mode 100644 index 00000000..c23ff4f6 --- /dev/null +++ b/tests/test_resource_consumers.py @@ -0,0 +1,180 @@ +"""Tests for classes that consume bundled OrthoEvol resources.""" + +import os +import tempfile +import unittest +from pathlib import Path +from shutil import rmtree +from types import SimpleNamespace +from unittest import mock + +from OrthoEvol.Manager.config import yml +from OrthoEvol.Manager.biosql.biosql import BaseBioSQL +from OrthoEvol.Manager.biosql.biosql_repo import scripts as sql_scripts +from OrthoEvol.Manager.data_management import DataMana +from OrthoEvol.Orthologs.Blast.comparative_genetics import ( + BaseComparativeGenetics, +) +from OrthoEvol.Orthologs.Phylogenetics.PAML.codeml import CodemlRun +from OrthoEvol.Tools.pbs.qstat import BaseQstat +from OrthoEvol.Tools.pbs.qsub import Qsub + + +class TestResourceConsumers(unittest.TestCase): + """Verify callers resolve the bundled files they depend on.""" + + def setUp(self) -> None: + """Create an isolated filesystem for constructor smoke tests.""" + self.test_dir = Path(tempfile.mkdtemp()) + self.addCleanup(rmtree, self.test_dir, ignore_errors=True) + + @mock.patch("OrthoEvol.Manager.data_management.package_resource_path") + def test_data_manager_selects_pipeline_config( + self, + mock_resource_path: mock.Mock, + ) -> None: + """Select the correct bundled configuration for new and existing runs.""" + expected_calls = ( + (True, "pipeline.yml"), + (False, "initialize_old.yml"), + ) + + for is_new, expected_name in expected_calls: + with self.subTest(is_new=is_new): + DataMana(pipeline="Ortho_CDS_1", new=is_new) + + mock_resource_path.assert_called_with(yml, expected_name) + + @mock.patch("OrthoEvol.Manager.biosql.biosql.package_resource_path") + def test_biosql_resolves_taxonomy_scripts( + self, + mock_resource_path: mock.Mock, + ) -> None: + """Resolve the BioSQL script directory and both taxonomy loaders.""" + mock_resource_path.side_effect = ( + self.test_dir, + self.test_dir / "load_ncbi_taxonomy.pl", + self.test_dir / "load_itis_taxonomy.pl", + ) + + biosql = BaseBioSQL( + database_name="test.db", + project="test-project", + project_path=self.test_dir, + proj_mana=None, + ) + + self.assertEqual(biosql.scripts, self.test_dir) + self.assertEqual( + biosql.ncbi_taxon_script, + self.test_dir / "load_ncbi_taxonomy.pl", + ) + self.assertEqual( + biosql.itis_taxon_script, + self.test_dir / "load_itis_taxonomy.pl", + ) + self.assertEqual( + mock_resource_path.call_args_list, + [ + mock.call(sql_scripts), + mock.call(sql_scripts, "load_ncbi_taxonomy.pl"), + mock.call(sql_scripts, "load_itis_taxonomy.pl"), + ], + ) + + def test_scheduler_classes_resolve_configuration_files(self) -> None: + """Resolve scheduler configuration without invoking cluster commands.""" + qstat_config = self.test_dir / "qstat.yml" + pbs_template = self.test_dir / "temp.pbs" + + with mock.patch( + "OrthoEvol.Tools.pbs.qstat.package_resource_path", + return_value=qstat_config, + ) as mock_qstat_resource: + qstat = BaseQstat(job_id="123.server", home=self.test_dir / "qstat") + + with mock.patch( + "OrthoEvol.Tools.pbs.qsub.package_resource_path", + return_value=pbs_template, + ) as mock_qsub_resource: + qsub = Qsub( + job_name="test-job", + base_job_id="abcde", + pbs_working_dir=self.test_dir, + python_script="analysis.py", + pbs_command_list=[], + ) + + self.assertEqual(qstat._yaml_config, qstat_config) + self.assertEqual(qsub.pbs_template, pbs_template) + mock_qstat_resource.assert_called_once() + mock_qsub_resource.assert_called_once() + + @mock.patch( + "OrthoEvol.Orthologs.Blast.comparative_genetics.shutil.copy" + ) + @mock.patch( + "OrthoEvol.Orthologs.Blast.comparative_genetics.package_resource_path" + ) + @mock.patch( + "OrthoEvol.Orthologs.Blast.comparative_genetics.FullUtilities" + ) + def test_comparative_genetics_resolves_packaged_accessions( + self, + mock_utilities: mock.Mock, + mock_resource_path: mock.Mock, + mock_copy: mock.Mock, + ) -> None: + """Resolve a packaged accession file before copying it into a project.""" + accession_path = self.test_dir / "accessions.csv" + mock_resource_path.return_value = accession_path + mock_utilities.return_value.attribute_config.return_value = SimpleNamespace( + project_index=self.test_dir + ) + + BaseComparativeGenetics( + project="test-project", + project_path=self.test_dir, + acc_file=None, + proj_mana=None, + copy_from_package=True, + ) + + mock_resource_path.assert_called_once() + mock_copy.assert_called_once_with(accession_path, str(self.test_dir)) + + @mock.patch( + "OrthoEvol.Orthologs.Phylogenetics.PAML.codeml.package_resource_path" + ) + @mock.patch("OrthoEvol.Orthologs.Phylogenetics.PAML.codeml.copy") + @mock.patch("OrthoEvol.Orthologs.Phylogenetics.PAML.codeml.codeml.Codeml") + def test_codeml_resolves_control_template( + self, + mock_codeml: mock.Mock, + mock_copy: mock.Mock, + mock_resource_path: mock.Mock, + ) -> None: + """Resolve the bundled control template before configuring Codeml.""" + original_directory = Path.cwd() + self.addCleanup(os.chdir, original_directory) + control_template = self.test_dir / "codeml.ctl" + mock_resource_path.return_value = control_template + mock_copy.side_effect = lambda source, destination: str( + Path(destination) / Path(source).name + ) + + codeml_run = CodemlRun( + P2N_alignment="alignment.phy", + iqtree_newick="gene_iqtree.nwk", + home=self.test_dir, + ) + + self.assertEqual(codeml_run.control_template, control_template) + mock_resource_path.assert_called_once() + mock_codeml.return_value.read_ctl_file.assert_called_once_with( + control_template + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_tools.py b/tests/test_tools.py index 1dd83c9e..268667ee 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -5,8 +5,8 @@ import os import logging import tempfile -from pkg_resources import resource_filename +from OrthoEvol.resources import package_resource_path from OrthoEvol.Tools.logit import LogIt from OrthoEvol.Tools.parallel import Multiprocess from OrthoEvol.Tools.ftp import NcbiFTPClient @@ -20,7 +20,7 @@ class TestTools(unittest.TestCase): def setUp(self, logfile='test.log'): self.logfile = logfile - self.infile = resource_filename(test.__name__, 'test_blast.csv') + self.infile = package_resource_path(test, 'test_blast.csv') self.outfile = 'out_mygene.csv' def test_logit(self): diff --git a/tests/test_utils.py b/tests/test_utils.py index d37510f4..5ec93d55 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -300,10 +300,10 @@ def test_parse_db_config_file(self): class TestPackageVersion(unittest.TestCase): - def test_init(self): + def test_init(self) -> None: """Test PackageVersion initialization.""" - pv = PackageVersion('setuptools') - self.assertEqual(pv.packagename, 'setuptools') + pv = PackageVersion('OrthoEvol') + self.assertEqual(pv.packagename, 'OrthoEvol') self.assertIsNotNone(pv)