Skip to content

Commit

Permalink
Merge PR #1315: Fix typechecking for top-level samtools commands
Browse files Browse the repository at this point in the history
  • Loading branch information
jmarshall committed Oct 24, 2024
2 parents ed699eb + 256adfc commit effb935
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 264 deletions.
93 changes: 30 additions & 63 deletions pysam/bcftools.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,32 @@
try:
from typing import Final
HAVE_FINAL = True
except ImportError:
HAVE_FINAL = False
import pysam.utils

from pysam.utils import PysamDispatcher
annotate = pysam.utils.PysamDispatcher('bcftools', 'annotate')
call = pysam.utils.PysamDispatcher('bcftools', 'call')
cnv = pysam.utils.PysamDispatcher('bcftools', 'cnv')
concat = pysam.utils.PysamDispatcher('bcftools', 'concat')
consensus = pysam.utils.PysamDispatcher('bcftools', 'consensus')
convert = pysam.utils.PysamDispatcher('bcftools', 'convert')
csq = pysam.utils.PysamDispatcher('bcftools', 'csq')
filter = pysam.utils.PysamDispatcher('bcftools', 'filter')
gtcheck = pysam.utils.PysamDispatcher('bcftools', 'gtcheck')
head = pysam.utils.PysamDispatcher('bcftools', 'head')
index = pysam.utils.PysamDispatcher('bcftools', 'index')
isec = pysam.utils.PysamDispatcher('bcftools', 'isec')
merge = pysam.utils.PysamDispatcher('bcftools', 'merge')
mpileup = pysam.utils.PysamDispatcher('bcftools', 'mpileup')
norm = pysam.utils.PysamDispatcher('bcftools', 'norm')
plugin = pysam.utils.PysamDispatcher('bcftools', 'plugin')
query = pysam.utils.PysamDispatcher('bcftools', 'query')
reheader = pysam.utils.PysamDispatcher('bcftools', 'reheader')
roh = pysam.utils.PysamDispatcher('bcftools', 'roh')
sort = pysam.utils.PysamDispatcher('bcftools', 'sort')
stats = pysam.utils.PysamDispatcher('bcftools', 'stats')
view = pysam.utils.PysamDispatcher('bcftools', 'view')

_BCFTOOLS_DISPATCH = [
"index",
"annotate",
"concat",
"convert",
"isec",
"merge",
"norm",
"plugin",
"query",
"reheader",
"sort",
"view",
"head",
"call",
"consensus",
"cnv",
"csq",
"filter",
"gtcheck",
"mpileup",
"roh",
"stats"]


def _wrap_command(dispatch: str) -> PysamDispatcher:
return PysamDispatcher("bcftools", dispatch, ())


if not HAVE_FINAL:
# instantiate bcftools commands as python functions
for cmd in _BCFTOOLS_DISPATCH:
globals()[cmd] = PysamDispatcher("bcftools", cmd, None)
else:
# python >=3.8
index: Final[PysamDispatcher] = _wrap_command("index")
annotate: Final[PysamDispatcher] = _wrap_command("annotate")
concat: Final[PysamDispatcher] = _wrap_command("concat")
convert: Final[PysamDispatcher] = _wrap_command("convert")
isec: Final[PysamDispatcher] = _wrap_command("isec")
merge: Final[PysamDispatcher] = _wrap_command("merge")
norm: Final[PysamDispatcher] = _wrap_command("norm")
plugin: Final[PysamDispatcher] = _wrap_command("plugin")
query: Final[PysamDispatcher] = _wrap_command("query")
reheader: Final[PysamDispatcher] = _wrap_command("reheader")
sort: Final[PysamDispatcher] = _wrap_command("sort")
view: Final[PysamDispatcher] = _wrap_command("view")
head: Final[PysamDispatcher] = _wrap_command("head")
call: Final[PysamDispatcher] = _wrap_command("call")
consensus: Final[PysamDispatcher] = _wrap_command("consensus")
cnv: Final[PysamDispatcher] = _wrap_command("cnv")
csq: Final[PysamDispatcher] = _wrap_command("csq")
filter: Final[PysamDispatcher] = _wrap_command("filter")
gtcheck: Final[PysamDispatcher] = _wrap_command("gtcheck")
mpileup: Final[PysamDispatcher] = _wrap_command("mpileup")
roh: Final[PysamDispatcher] = _wrap_command("roh")
stats: Final[PysamDispatcher] = _wrap_command("stats")
__all__ = [
'annotate', 'call', 'cnv', 'concat', 'consensus',
'convert', 'csq', 'filter', 'gtcheck', 'head',
'index', 'isec', 'merge', 'mpileup', 'norm',
'plugin', 'query', 'reheader', 'roh', 'sort',
'stats', 'view',
]
2 changes: 1 addition & 1 deletion pysam/libcutils.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ def _pysam_dispatch(collection,
if collection == "bcftools":
# in bcftools, most methods accept -o, the exceptions
# are below:
if method not in ("index", "roh", "stats"):
if method not in ("head", "index", "roh", "stats"):
stdout_option = "-o {}"
elif method in MAP_STDOUT_OPTIONS[collection]:
# special case - samtools view -c outputs on stdout
Expand Down
256 changes: 59 additions & 197 deletions pysam/samtools.py
Original file line number Diff line number Diff line change
@@ -1,197 +1,59 @@
import platform
from typing import (
Callable,
List,
Tuple,
Iterable,
Union,
)
try:
from typing import Final
HAVE_FINAL = True
except ImportError:
HAVE_FINAL = False

from pysam.utils import PysamDispatcher


# samtools command line options to export in python
_SAMTOOLS_DISPATCH = {
# samtools 'documented' commands
"view": ("view", ()),
"head": ("head", ()),
"sort": ("sort", ()),
"mpileup": ("mpileup", ()),
"consensus": ("consensus", ()),
"depth": ("depth", ()),
"faidx": ("faidx", ()),
"fqidx": ("fqidx", ()),
"tview": ("tview", ()),
"index": ("index", ()),
"idxstats": ("idxstats", ()),
"fixmate": ("fixmate", ()),
"flagstat": ("flagstat", ()),
"calmd": ("calmd", ()),
"merge": ("merge", ()),
"markdup": ("markdup", ()),
"rmdup": ("rmdup", ()),
"reference": ("reference", ()),
"reheader": ("reheader", ()),
"reset": ("reset", ()),
"cat": ("cat", ()),
"targetcut": ("targetcut", ()),
"phase": ("phase", ()),
"bam2fq": ("bam2fq", ()),
"dict": ("dict", ()),
"addreplacerg": ("addreplacerg", ()),
"pad2unpad": ("pad2unpad", ()),
"depad": ("pad2unpad", ()),
"bedcov": ("bedcov", ()),
"coverage": ("coverage", ()),
"bamshuf": ("bamshuf", ()),
"collate": ("collate", ()),
"stats": ("stats", ()),
"fasta": ("fasta", ()),
"fastq": ("fastq", ()),
"cram_size": ("cram-size", ()),
"quickcheck": ("quickcheck", ()),
"split": ("split", ()),
"flags": ("flags", ()),
"ampliconclip": ("ampliconclip", ()),
"ampliconstats": ("ampliconstats", ()),
"version": ("version", ()),
"fqimport": ("import", ()),
"import_": ("import", ()),
"samples": ("samples", ()),
}


def _wrap_command(
dispatch: str,
parsers: Iterable[Tuple[str, Callable[[Union[str, List[str]]], Union[str, List[str]]]]],
) -> PysamDispatcher:
return PysamDispatcher("samtools", dispatch, parsers)


if not HAVE_FINAL:
# python 3.7
for key, options in _SAMTOOLS_DISPATCH.items():
cmd, parser = options
globals()[key] = PysamDispatcher("samtools", cmd, parser)

__all__ = list(_SAMTOOLS_DISPATCH)
else:
# python >=3.8
view: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["view"][0], _SAMTOOLS_DISPATCH["view"][1])

head: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["head"][0], _SAMTOOLS_DISPATCH["head"][1])

sort: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["sort"][0], _SAMTOOLS_DISPATCH["sort"][1])

mpileup: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["mpileup"][0], _SAMTOOLS_DISPATCH["mpileup"][1])

consensus: Final[PysamDispatcher] = _wrap_command(
_SAMTOOLS_DISPATCH["consensus"][0],
_SAMTOOLS_DISPATCH["consensus"][1],
)

depth: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["depth"][0], _SAMTOOLS_DISPATCH["depth"][1])

faidx: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["faidx"][0], _SAMTOOLS_DISPATCH["faidx"][1])

fqidx: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["fqidx"][0], _SAMTOOLS_DISPATCH["fqidx"][1])

tview: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["tview"][0], _SAMTOOLS_DISPATCH["tview"][1])

index: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["index"][0], _SAMTOOLS_DISPATCH["index"][1])

idxstats: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["idxstats"][0], _SAMTOOLS_DISPATCH["idxstats"][1])

fixmate: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["fixmate"][0], _SAMTOOLS_DISPATCH["fixmate"][1])

flagstat: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["flagstat"][0], _SAMTOOLS_DISPATCH["flagstat"][1])

calmd: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["calmd"][0], _SAMTOOLS_DISPATCH["calmd"][1])

merge: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["merge"][0], _SAMTOOLS_DISPATCH["merge"][1])

markdup: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["markdup"][0], _SAMTOOLS_DISPATCH["markdup"][1])

rmdup: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["rmdup"][0], _SAMTOOLS_DISPATCH["rmdup"][1])

reference: Final[PysamDispatcher] = _wrap_command(
_SAMTOOLS_DISPATCH["reference"][0],
_SAMTOOLS_DISPATCH["reference"][1],
)

reheader: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["reheader"][0], _SAMTOOLS_DISPATCH["reheader"][1])

reset: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["reset"][0], _SAMTOOLS_DISPATCH["reset"][1])

cat: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["cat"][0], _SAMTOOLS_DISPATCH["cat"][1])

targetcut: Final[PysamDispatcher] = _wrap_command(
_SAMTOOLS_DISPATCH["targetcut"][0],
_SAMTOOLS_DISPATCH["targetcut"][1],
)

phase: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["phase"][0], _SAMTOOLS_DISPATCH["phase"][1])

bam2fq: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["bam2fq"][0], _SAMTOOLS_DISPATCH["bam2fq"][1])

dict: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["dict"][0], _SAMTOOLS_DISPATCH["dict"][1])

addreplacerg: Final[PysamDispatcher] = _wrap_command(
_SAMTOOLS_DISPATCH["addreplacerg"][0],
_SAMTOOLS_DISPATCH["addreplacerg"][1],
)

pad2unpad: Final[PysamDispatcher] = _wrap_command(
_SAMTOOLS_DISPATCH["pad2unpad"][0],
_SAMTOOLS_DISPATCH["pad2unpad"][1],
)

depad: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["depad"][0], _SAMTOOLS_DISPATCH["depad"][1])

bedcov: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["bedcov"][0], _SAMTOOLS_DISPATCH["bedcov"][1])

coverage: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["coverage"][0], _SAMTOOLS_DISPATCH["coverage"][1])

bamshuf: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["bamshuf"][0], _SAMTOOLS_DISPATCH["bamshuf"][1])

collate: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["collate"][0], _SAMTOOLS_DISPATCH["collate"][1])

stats: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["stats"][0], _SAMTOOLS_DISPATCH["stats"][1])

fasta: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["fasta"][0], _SAMTOOLS_DISPATCH["fasta"][1])

fastq: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["fastq"][0], _SAMTOOLS_DISPATCH["fastq"][1])

cram_size: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["cram_size"][0], _SAMTOOLS_DISPATCH["cram_size"][1])

quickcheck: Final[PysamDispatcher] = _wrap_command(
_SAMTOOLS_DISPATCH["quickcheck"][0],
_SAMTOOLS_DISPATCH["quickcheck"][1],
)

split: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["split"][0], _SAMTOOLS_DISPATCH["split"][1])

flags: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["flags"][0], _SAMTOOLS_DISPATCH["flags"][1])

ampliconclip: Final[PysamDispatcher] = _wrap_command(
_SAMTOOLS_DISPATCH["ampliconclip"][0],
_SAMTOOLS_DISPATCH["ampliconclip"][1],
)

ampliconstats: Final[PysamDispatcher] = _wrap_command(
_SAMTOOLS_DISPATCH["ampliconstats"][0],
_SAMTOOLS_DISPATCH["ampliconstats"][1],
)

version: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["version"][0], _SAMTOOLS_DISPATCH["version"][1])

fqimport: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["fqimport"][0], _SAMTOOLS_DISPATCH["fqimport"][1])

import_: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["import_"][0], _SAMTOOLS_DISPATCH["import_"][1])

samples: Final[PysamDispatcher] = _wrap_command(_SAMTOOLS_DISPATCH["samples"][0], _SAMTOOLS_DISPATCH["samples"][1])
import pysam.utils

addreplacerg = pysam.utils.PysamDispatcher('samtools', 'addreplacerg')
ampliconclip = pysam.utils.PysamDispatcher('samtools', 'ampliconclip')
ampliconstats = pysam.utils.PysamDispatcher('samtools', 'ampliconstats')
bam2fq = pysam.utils.PysamDispatcher('samtools', 'bam2fq')
bamshuf = pysam.utils.PysamDispatcher('samtools', 'bamshuf')
bedcov = pysam.utils.PysamDispatcher('samtools', 'bedcov')
calmd = pysam.utils.PysamDispatcher('samtools', 'calmd')
cat = pysam.utils.PysamDispatcher('samtools', 'cat')
collate = pysam.utils.PysamDispatcher('samtools', 'collate')
consensus = pysam.utils.PysamDispatcher('samtools', 'consensus')
coverage = pysam.utils.PysamDispatcher('samtools', 'coverage')
cram_size = pysam.utils.PysamDispatcher('samtools', 'cram-size')
depad = pysam.utils.PysamDispatcher('samtools', 'depad')
depth = pysam.utils.PysamDispatcher('samtools', 'depth')
dict = pysam.utils.PysamDispatcher('samtools', 'dict')
faidx = pysam.utils.PysamDispatcher('samtools', 'faidx')
fasta = pysam.utils.PysamDispatcher('samtools', 'fasta')
fastq = pysam.utils.PysamDispatcher('samtools', 'fastq')
fixmate = pysam.utils.PysamDispatcher('samtools', 'fixmate')
flags = pysam.utils.PysamDispatcher('samtools', 'flags')
flagstat = pysam.utils.PysamDispatcher('samtools', 'flagstat')
fqidx = pysam.utils.PysamDispatcher('samtools', 'fqidx')
fqimport = pysam.utils.PysamDispatcher('samtools', 'import')
head = pysam.utils.PysamDispatcher('samtools', 'head')
idxstats = pysam.utils.PysamDispatcher('samtools', 'idxstats')
index = pysam.utils.PysamDispatcher('samtools', 'index')
markdup = pysam.utils.PysamDispatcher('samtools', 'markdup')
merge = pysam.utils.PysamDispatcher('samtools', 'merge')
mpileup = pysam.utils.PysamDispatcher('samtools', 'mpileup')
pad2unpad = pysam.utils.PysamDispatcher('samtools', 'pad2unpad')
phase = pysam.utils.PysamDispatcher('samtools', 'phase')
quickcheck = pysam.utils.PysamDispatcher('samtools', 'quickcheck')
reference = pysam.utils.PysamDispatcher('samtools', 'reference')
reheader = pysam.utils.PysamDispatcher('samtools', 'reheader')
reset = pysam.utils.PysamDispatcher('samtools', 'reset')
rmdup = pysam.utils.PysamDispatcher('samtools', 'rmdup')
samples = pysam.utils.PysamDispatcher('samtools', 'samples')
sort = pysam.utils.PysamDispatcher('samtools', 'sort')
split = pysam.utils.PysamDispatcher('samtools', 'split')
stats = pysam.utils.PysamDispatcher('samtools', 'stats')
targetcut = pysam.utils.PysamDispatcher('samtools', 'targetcut')
tview = pysam.utils.PysamDispatcher('samtools', 'tview')
version = pysam.utils.PysamDispatcher('samtools', 'version')
view = pysam.utils.PysamDispatcher('samtools', 'view')

__all__ = [
'addreplacerg', 'ampliconclip', 'ampliconstats',
'bam2fq', 'bamshuf', 'bedcov', 'calmd', 'cat',
'collate', 'consensus', 'coverage', 'cram_size',
'depad', 'depth', 'dict', 'faidx', 'fasta',
'fastq', 'fixmate', 'flags', 'flagstat', 'fqidx',
'fqimport', 'head', 'idxstats', 'index',
'markdup', 'merge', 'mpileup', 'pad2unpad',
'phase', 'quickcheck', 'reference', 'reheader',
'reset', 'rmdup', 'samples', 'sort', 'split',
'stats', 'targetcut', 'tview', 'version', 'view',
]
3 changes: 2 additions & 1 deletion pysam/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import (
Callable,
List,
Optional,
Tuple,
Iterable,
Union,
Expand Down Expand Up @@ -48,7 +49,7 @@ def __init__(
self,
collection: str,
dispatch: str,
parsers: Iterable[Tuple[str, Callable[[Union[str, List[str]]], Union[str, List[str]]]]],
parsers: Optional[Iterable[Tuple[str, Callable[[Union[str, List[str]]], Union[str, List[str]]]]]] = None,
):
self.collection = collection
self.dispatch = dispatch
Expand Down
2 changes: 0 additions & 2 deletions tests/samtools_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,6 @@ def check_statement(self, statement):

command = self.get_command(statement)

# self.assertTrue(command in pysam.SAMTOOLS_DISPATCH)

targets = [x for x in parts if "%(out)s" in x]
samtools_targets = [x % r_samtools for x in targets]
pysam_targets = [x % r_pysam for x in targets]
Expand Down
Loading

0 comments on commit effb935

Please sign in to comment.