|
6 | 6 | """ |
7 | 7 |
|
8 | 8 | from pathlib import Path |
9 | | -from typing import List, Optional |
| 9 | +from typing import Annotated, List, Optional |
10 | 10 |
|
11 | 11 | import numpy as np |
12 | 12 | import typer |
13 | | -from typing_extensions import Annotated |
14 | 13 |
|
15 | 14 | from trx.io import load, save |
16 | 15 | from trx.trx_file_memmap import TrxFile, concatenate, load as load_trx |
|
25 | 24 | verify_header_compatibility, |
26 | 25 | ) |
27 | 26 |
|
| 27 | + |
| 28 | +def _debug_callback(value: bool) -> None: |
| 29 | + """Print environment and dependency diagnostics, then exit. |
| 30 | +
|
| 31 | + Parameters |
| 32 | + ---------- |
| 33 | + value : bool |
| 34 | + Whether the ``--debug`` flag was passed. |
| 35 | + """ |
| 36 | + if not value: |
| 37 | + return |
| 38 | + |
| 39 | + import importlib.metadata |
| 40 | + import importlib.util |
| 41 | + import sys |
| 42 | + |
| 43 | + from trx import __version__ |
| 44 | + |
| 45 | + typer.echo("Environment diagnostics:") |
| 46 | + typer.echo(f" Python executable : {sys.executable}") |
| 47 | + typer.echo(f" sys.prefix : {sys.prefix}") |
| 48 | + typer.echo(f" trx-python version: {__version__}") |
| 49 | + |
| 50 | + trx_spec = importlib.util.find_spec("trx") |
| 51 | + trx_location = ( |
| 52 | + trx_spec.submodule_search_locations[0] |
| 53 | + if trx_spec and trx_spec.submodule_search_locations |
| 54 | + else "unknown" |
| 55 | + ) |
| 56 | + typer.echo(f" trx package : {trx_location}") |
| 57 | + |
| 58 | + # Read required dependencies from package metadata |
| 59 | + required_deps = [] |
| 60 | + try: |
| 61 | + import re |
| 62 | + |
| 63 | + for req in importlib.metadata.requires("trx-python") or []: |
| 64 | + # Skip optional / extra deps (they contain "; extra ==") |
| 65 | + if "extra ==" in req: |
| 66 | + continue |
| 67 | + # Extract the package name (strip version specifiers like >=, <=, ~=) |
| 68 | + dep_name = re.split(r"[>=<!~;\s]", req)[0] |
| 69 | + required_deps.append(dep_name) |
| 70 | + except importlib.metadata.PackageNotFoundError: |
| 71 | + required_deps = ["deepdiff", "nibabel", "numpy", "typer"] |
| 72 | + |
| 73 | + optional_deps = ["dipy", "fury", "vtk"] |
| 74 | + |
| 75 | + typer.echo("\nRequired dependencies:") |
| 76 | + for dep in required_deps: |
| 77 | + spec = importlib.util.find_spec(dep) |
| 78 | + if spec is None: |
| 79 | + typer.echo(f" {dep:12s} NOT FOUND") |
| 80 | + else: |
| 81 | + typer.echo(f" {dep:12s} found") |
| 82 | + |
| 83 | + typer.echo("\nOptional dependencies:") |
| 84 | + for dep in optional_deps: |
| 85 | + spec = importlib.util.find_spec(dep) |
| 86 | + if spec is None: |
| 87 | + typer.echo(f" {dep:12s} not found") |
| 88 | + else: |
| 89 | + typer.echo(f" {dep:12s} found") |
| 90 | + |
| 91 | + raise typer.Exit() |
| 92 | + |
| 93 | + |
| 94 | +def _main_callback( |
| 95 | + _debug: Annotated[ |
| 96 | + bool, |
| 97 | + typer.Option( |
| 98 | + "--debug", |
| 99 | + help="Print environment and dependency diagnostics.", |
| 100 | + callback=_debug_callback, |
| 101 | + is_eager=True, |
| 102 | + ), |
| 103 | + ] = False, |
| 104 | +) -> None: |
| 105 | + """TRX File Format Tools - CLI for brain tractography data manipulation. |
| 106 | +
|
| 107 | + Parameters |
| 108 | + ---------- |
| 109 | + _debug : bool, optional |
| 110 | + If True, print environment and dependency diagnostics and exit. |
| 111 | + """ |
| 112 | + |
| 113 | + |
28 | 114 | app = typer.Typer( |
29 | 115 | name="trx", |
30 | 116 | help="TRX File Format Tools - CLI for brain tractography data manipulation.", |
31 | 117 | add_completion=False, |
32 | 118 | rich_markup_mode="rich", |
| 119 | + callback=_main_callback, |
33 | 120 | ) |
34 | 121 |
|
35 | 122 |
|
|
0 commit comments