Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions .github/workflows/lint.yml

This file was deleted.

20 changes: 9 additions & 11 deletions .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@ on:
pull_request: []

jobs:
lint:
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v3
- run: ruff format
build:

needs: lint
runs-on: ubuntu-latest
strategy:
max-parallel: 4
Expand All @@ -18,7 +26,6 @@ jobs:
- "3.11"
- "3.12"
- "3.13"

steps:
- name: Install apt packages
run: |
Expand Down Expand Up @@ -46,15 +53,6 @@ jobs:
echo $poetryv
- name: Install package for testing
run: poetry install -v --all-extras
- name: Lint with flake8
run: |
pipx install flake8
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings
# The GitHub editor is 127 chars wide
flake8 . --count --ignore=E302 --exit-zero --max-complexity=10 \
--max-line-length=127 --statistics
- name: Test with pytest
run: poetry run pytest --script-launch-mode=subprocess
- name: Reinstall package for production
Expand Down
32 changes: 29 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ tact_add_config = "tact.cli_add_toml:main"
tact_build_taxonomic_tree = "tact.cli_taxonomy:main"
tact_check_results = "tact.cli_check_trees:main"


[tool.poetry]
include = ["examples"]
classifiers = [
Expand All @@ -56,9 +55,36 @@ classifiers = [
"Topic :: Scientific/Engineering :: Bio-Informatics"
]

[tool.black]
[tool.ruff]
line-length = 118
target-version = ['py38', 'py39', 'py310', 'py311']

[tool.ruff.lint]
fixable = ["ALL"]
select = [
"NPY", # numpy warnings
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"D", # pydocstyle
"DOC", # pydoclint
"I", # isort
"RUF", # ruff
"B", # flake8-bugbear
"UP", # pyupgrade
"ICN", # flake8-import-conventions
"FA", # flake8-future-annotations
"C4", # flake8-comprehensions
"A" # flake8-builtins
]

[tool.ruff.lint.per-file-ignores]
# Ignore all directories named `tests` and CLI modules.
"tests/**" = ["D", "DOC"]
"tact/cli_*" = ["D", "DOC"]
"__init__.py" = ["D", "DOC"]

[tool.ruff.lint.pydocstyle]
convention = "google"

[build-system]
requires = ["poetry_core>=2.0"]
Expand Down
44 changes: 18 additions & 26 deletions tact/cli_add_taxa.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Try to assign tips to a pre-existing tree based on a taxonomy
# Jonathan Chang, May 13, 2016

from __future__ import division
from __future__ import print_function

import csv
import logging
Expand All @@ -18,21 +15,20 @@
import dendropy

from . import fastmrca
from .lib import crown_capture_probability
from .lib import get_new_times
from .tree_util import get_ages
from .tree_util import get_birth_death_rates
from .tree_util import get_min_age
from .tree_util import get_short_branches
from .tree_util import get_tip_labels
from .tree_util import graft_node
from .tree_util import is_binary
from .tree_util import is_fully_locked
from .tree_util import lock_clade
from .tree_util import update_tree_view
from .validation import validate_outgroups
from .validation import validate_taxonomy_tree
from .validation import BackboneCommand
from .lib import crown_capture_probability, get_new_times
from .tree_util import (
get_ages,
get_birth_death_rates,
get_min_age,
get_short_branches,
get_tip_labels,
graft_node,
is_binary,
is_fully_locked,
lock_clade,
update_tree_view,
)
from .validation import BackboneCommand, validate_outgroups, validate_taxonomy_tree

logger = logging.getLogger(__name__)
# Speed up logging for PyPy
Expand Down Expand Up @@ -87,9 +83,7 @@ def search_ancestors_for_valid_backbone_node(taxonomy_node, backbone_tips, ccp):


def get_new_branching_times(backbone_node, taxonomy_node, told=None, tyoung=0, min_ccp=0.8, num_new_times=None):
"""
Get `n_total` new branching times for a `node`.
"""
"""Get `n_total` new branching times for a `node`."""
global mrca_rates
taxon = taxonomy_node.label
birth, death, ccp, _ = mrca_rates[taxon]
Expand Down Expand Up @@ -131,7 +125,7 @@ def get_new_branching_times(backbone_node, taxonomy_node, told=None, tyoung=0, m


def fill_new_taxa(namespace, node, new_taxa, times, stem=False):
for new_species, new_age in zip(new_taxa, times):
for new_species, new_age in zip(new_taxa, times, strict=True):
new_node = dendropy.Node()
new_node.annotations.add_new("creation_method", "fill_new_taxa")
new_node.age = new_age
Expand Down Expand Up @@ -184,7 +178,7 @@ def create_clade(namespace, species, ages):
# Lock the child of the seed node so that things can still attach to the stem of this new clade
lock_clade(tree.seed_node.child_nodes()[0])
if list(get_short_branches(tree.seed_node)):
logger.info("{} short branches detected".format(len(list(get_short_branches(tree.seed_node)))))
logger.info(f"{len(list(get_short_branches(tree.seed_node)))} short branches detected")
return tree


Expand Down Expand Up @@ -315,9 +309,7 @@ def run_precalcs(taxonomy_tree, backbone_tree, min_ccp=0.8, yule=False):
)
@click.option("-v", "--verbose", help="emit extra information (can be repeated)", count=True)
def main(taxonomy, backbone, outgroups, output, min_ccp, verbose, yule, ultrametricity_precision):
"""
Add tips onto a BACKBONE phylogeny using a TAXONOMY phylogeny.
"""
"""Add tips onto a BACKBONE phylogeny using a TAXONOMY phylogeny."""
logger.addHandler(logging.FileHandler(output + ".log.txt"))
if verbose >= 2:
logger.setLevel(logging.DEBUG)
Expand Down
51 changes: 25 additions & 26 deletions tact/cli_add_toml.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,34 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Try to assign tips to a pre-existing tree based on a TOML configuration file
# Jonathan Chang, Aug 14, 2021
"""Command line interface to assign tips to a pre-existing tree based on a TOML configuration file."""

from __future__ import annotations

from concurrent.futures import ProcessPoolExecutor, as_completed
from dataclasses import dataclass, field, InitVar
from collections import defaultdict
import copy
import logging
import operator
import os
import re
import sys
import typing
from collections import defaultdict
from concurrent.futures import ProcessPoolExecutor, as_completed
from dataclasses import InitVar, dataclass, field
from functools import reduce

import click
import dendropy
import toml

from .lib import get_new_times
from .tree_util import get_ages
from .tree_util import get_birth_death_rates
from .tree_util import get_min_age
from .tree_util import get_tip_labels
from .tree_util import graft_node
from .tree_util import is_binary
from .tree_util import lock_clade
from .tree_util import unlock_clade
from .tree_util import update_tree_view
from .tree_util import (
get_ages,
get_birth_death_rates,
get_min_age,
get_tip_labels,
graft_node,
is_binary,
lock_clade,
unlock_clade,
update_tree_view,
)
from .validation import BackboneCommand

logger = logging.getLogger(__name__)
Expand All @@ -42,9 +41,9 @@

@dataclass
class TactConstraint:
"""Class for keeping track of a constraint in TACT (positive or negative)"""
"""Class for keeping track of a constraint in TACT (positive or negative)."""

mrca: typing.List[str] = field(default_factory=list)
mrca: list[str] = field(default_factory=list)
stem: bool = False

def __post_init__(self):
Expand Down Expand Up @@ -91,6 +90,7 @@ def __post_init__(self, include, exclude):


def ensure_mrca(tree, tips, node=None):
"""Perform initial checks to ensure we can do MRCA calculations."""
try:
node = node if node else tree.seed_node
return tree.mrca(taxon_labels=tips, start_node=node)
Expand All @@ -112,7 +112,7 @@ def ensure_mrca(tree, tips, node=None):
def do_tact(tree, item):
# First, get the MRCA of _all_ `include` leafs. This is the basis of our rate computation,
# and how we actually implement polyphyletic groups.
included_tips = sum([x.mrca for x in item.include], [])
included_tips = reduce(operator.iadd, [x.mrca for x in item.include], [])
mrca_node = ensure_mrca(tree, included_tips)

# Compute the rates on that (possibly expansive) MRCA node.
Expand Down Expand Up @@ -143,7 +143,7 @@ def do_tact(tree, item):
genera_map[genus].add(tip)

if len(genera_map) > 1:
for genus, species in genera_map.items():
for species in genera_map.values():
node = tree.mrca(taxon_labels=species, start_node=inner_mrca_node)
if node and species == get_tip_labels(node):
if len(species) == 1:
Expand Down Expand Up @@ -178,6 +178,7 @@ def do_tact(tree, item):


def do_replicate(backbone, to_tact, label):
"""Perform a replicate of a TACT analysis."""
logger.info(f"<<< Replicate {label} >>>")
tree = copy.deepcopy(backbone)
for item in to_tact:
Expand Down Expand Up @@ -208,9 +209,7 @@ def do_replicate(backbone, to_tact, label):
default=os.cpu_count() or 1,
)
def main(config, backbone, output, verbose, ultrametricity_precision, replicates, cores):
"""
Add tips onto a BACKBONE phylogeny using a CONFIG file
"""
"""Add tips onto a BACKBONE phylogeny using a CONFIG file."""
logger.addHandler(logging.FileHandler(output + ".log.txt"))
if verbose >= 2:
logger.setLevel(logging.DEBUG)
Expand All @@ -226,7 +225,7 @@ def main(config, backbone, output, verbose, ultrametricity_precision, replicates
to_tact = [TactItem(**x) for x in config["tact"]]

# Ensure the proper ordering of TACT items based on divergence time of implied MRCA nodes
to_tact.sort(key=lambda item: ensure_mrca(backbone, sum([x.mrca for x in item.include], [])).age)
to_tact.sort(key=lambda ii: ensure_mrca(backbone, reduce(operator.iadd, [x.mrca for x in ii.include], [])).age)

# Compute global birth/death rates. Not currently used (but could be?)
backbone_tips = len(backbone.leaf_nodes())
Expand Down
16 changes: 5 additions & 11 deletions tact/cli_check_trees.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import division
"""Command-line interface module to check TACT results."""

import csv
import functools
Expand All @@ -12,13 +9,11 @@
import click
import dendropy

from .tree_util import get_birth_death_rates
from .tree_util import get_monophyletic_node
from .tree_util import get_tip_labels
from .tree_util import get_tree
from .tree_util import get_birth_death_rates, get_monophyletic_node, get_tip_labels, get_tree


def analyze_taxon(bb_tips, st_tips, backbone, simtaxed, taxon_node):
"""Perform various checks for a given taxon."""
taxon = taxon_node.label
if not taxon:
return None
Expand Down Expand Up @@ -89,14 +84,13 @@ def analyze_taxon(bb_tips, st_tips, backbone, simtaxed, taxon_node):
)
@click.option("--chunksize", help="number of tree nodes to allocate to each core", type=click.IntRange(1))
def main(simulated, backbone, taxonomy, output, cores, chunksize):
"""
Check a SIMULATED phylogeny for consistency with its backbone source tree and a taxonomy.
"""Check a SIMULATED phylogeny for consistency with its backbone source tree and a taxonomy.

The SIMULATED phylogeny should have been generated by the tact_add_taxa script.
All phylogenies should be in Newick format.
"""
pool = multiprocessing.Pool(processes=cores)
click.echo("Using %d parallel cores" % cores, err=True)
click.echo(f"Using {cores} parallel cores", err=True)
taxonomy = dendropy.Tree.get_from_path(taxonomy, schema="newick")
tn = taxonomy.taxon_namespace
click.echo("Taxonomy OK", err=True)
Expand Down
Loading
Loading