bactopia 2.1.2__tar.gz → 2.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. {bactopia-2.1.2 → bactopia-2.1.3}/PKG-INFO +1 -1
  2. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/atb/atb_downloader.py +3 -15
  3. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/atb/atb_formatter.py +3 -15
  4. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/catalog.py +31 -15
  5. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/citations.py +30 -6
  6. bactopia-2.1.3/bactopia/cli/common.py +37 -0
  7. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/datasets.py +4 -16
  8. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/docs.py +38 -8
  9. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/download.py +9 -19
  10. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/helpers/merge_schemas.py +4 -16
  11. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/lint.py +8 -15
  12. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/prepare.py +5 -15
  13. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/prune.py +8 -22
  14. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/pubmlst/build.py +11 -28
  15. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/pubmlst/setup.py +6 -23
  16. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/review.py +13 -23
  17. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/scaffold.py +38 -51
  18. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/search.py +5 -15
  19. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/status.py +7 -17
  20. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/summary.py +5 -15
  21. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/testing.py +20 -32
  22. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/update.py +7 -17
  23. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/workflows.py +6 -16
  24. {bactopia-2.1.2 → bactopia-2.1.3}/pyproject.toml +1 -1
  25. bactopia-2.1.2/bactopia/cli/jsonify.py +0 -85
  26. {bactopia-2.1.2 → bactopia-2.1.3}/LICENSE +0 -0
  27. {bactopia-2.1.2 → bactopia-2.1.3}/README.md +0 -0
  28. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/__init__.py +0 -0
  29. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/atb.py +0 -0
  30. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/__init__.py +0 -0
  31. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/atb/__init__.py +0 -0
  32. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/helpers/__init__.py +0 -0
  33. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/pipeline/__init__.py +0 -0
  34. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/pipeline/bracken_to_excel.py +0 -0
  35. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/pipeline/check_assembly_accession.py +0 -0
  36. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/pipeline/check_fastqs.py +0 -0
  37. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/pipeline/cleanup_coverage.py +0 -0
  38. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/pipeline/kraken_bracken_summary.py +0 -0
  39. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/pipeline/mask_consensus.py +0 -0
  40. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/pipeline/scrubber_summary.py +0 -0
  41. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/pipeline/teton_prepare.py +0 -0
  42. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/cli/sysinfo.py +0 -0
  43. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/conda.py +0 -0
  44. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/databases/__init__.py +0 -0
  45. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/databases/ena.py +0 -0
  46. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/databases/ncbi.py +0 -0
  47. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/databases/pubmlst/__init__.py +0 -0
  48. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/databases/pubmlst/constants.py +0 -0
  49. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/databases/pubmlst/utils.py +0 -0
  50. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/lint/__init__.py +0 -0
  51. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/lint/citations.py +0 -0
  52. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/lint/docs.py +0 -0
  53. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/lint/models.py +0 -0
  54. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/lint/rules/__init__.py +0 -0
  55. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/lint/rules/module_rules.py +0 -0
  56. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/lint/rules/subworkflow_rules.py +0 -0
  57. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/lint/rules/workflow_rules.py +0 -0
  58. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/lint/runner.py +0 -0
  59. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/nf.py +0 -0
  60. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/outputs.py +0 -0
  61. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parse.py +0 -0
  62. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/__init__.py +0 -0
  63. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/amrfinderplus.py +0 -0
  64. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/annotator.py +0 -0
  65. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/ariba.py +0 -0
  66. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/assembler.py +0 -0
  67. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/blast.py +0 -0
  68. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/citations.py +0 -0
  69. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/coverage.py +0 -0
  70. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/error.py +0 -0
  71. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/gather.py +0 -0
  72. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/generic.py +0 -0
  73. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/kraken.py +0 -0
  74. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/mapping.py +0 -0
  75. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/mlst.py +0 -0
  76. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/nextflow.py +0 -0
  77. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/parsables.py +0 -0
  78. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/qc.py +0 -0
  79. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/sketcher.py +0 -0
  80. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/variants.py +0 -0
  81. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/versions.py +0 -0
  82. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/parsers/workflows.py +0 -0
  83. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/reports/__init__.py +0 -0
  84. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/reports/templates/__init__.py +0 -0
  85. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/scaffold.py +0 -0
  86. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/summary.py +0 -0
  87. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/__init__.py +0 -0
  88. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/bactopia/llms.txt.j2 +0 -0
  89. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/logos.py +0 -0
  90. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/nextflow/nextflow.config.j2 +0 -0
  91. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/nextflow/params.config.j2 +0 -0
  92. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/nextflow/process.config.j2 +0 -0
  93. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/scaffold/module/main.nf.j2 +0 -0
  94. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/scaffold/module/module.config.j2 +0 -0
  95. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/scaffold/module/schema.json.j2 +0 -0
  96. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/scaffold/module/tests/main.nf.test.j2 +0 -0
  97. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/scaffold/module/tests/nextflow.config.j2 +0 -0
  98. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/scaffold/module/tests/nf-test.config.j2 +0 -0
  99. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/scaffold/subworkflow/main.nf.j2 +0 -0
  100. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/scaffold/subworkflow/tests/main.nf.test.j2 +0 -0
  101. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/scaffold/subworkflow/tests/nextflow.config.j2 +0 -0
  102. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/scaffold/subworkflow/tests/nf-test.config.j2 +0 -0
  103. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/scaffold/subworkflow/tests/nftignore.j2 +0 -0
  104. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/scaffold/workflow/main.nf.j2 +0 -0
  105. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/scaffold/workflow/nextflow.config.j2 +0 -0
  106. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/scaffold/workflow/tests/main.nf.test.j2 +0 -0
  107. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/scaffold/workflow/tests/nf-test.config.j2 +0 -0
  108. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/templates/scaffold/workflow/tests/nftignore.j2 +0 -0
  109. {bactopia-2.1.2 → bactopia-2.1.3}/bactopia/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bactopia
3
- Version: 2.1.2
3
+ Version: 2.1.3
4
4
  Summary: A Python package for working with Bactopia
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -6,10 +6,10 @@ import rich
6
6
  import rich.console
7
7
  import rich.traceback
8
8
  import rich_click as click
9
- from rich.logging import RichHandler
10
9
 
11
10
  import bactopia
12
11
  from bactopia.atb import parse_atb_file_list
12
+ from bactopia.cli.common import common_options, setup_logging
13
13
  from bactopia.databases.ncbi import is_biosample, taxid2name
14
14
  from bactopia.utils import (
15
15
  download_url,
@@ -66,7 +66,7 @@ click.rich_click.OPTION_GROUPS = {
66
66
 
67
67
 
68
68
  @click.command()
69
- @click.version_option(bactopia.__version__, "--version", "-V")
69
+ @common_options
70
70
  @click.option(
71
71
  "--query",
72
72
  "-q",
@@ -130,8 +130,6 @@ click.rich_click.OPTION_GROUPS = {
130
130
  help="The size of the chunks to split the list into",
131
131
  )
132
132
  @click.option("--force", is_flag=True, help="Overwrite existing files")
133
- @click.option("--verbose", is_flag=True, help="Increase the verbosity of output")
134
- @click.option("--silent", is_flag=True, help="Only critical errors will be printed")
135
133
  def atb_downloader(
136
134
  query,
137
135
  outdir,
@@ -148,17 +146,7 @@ def atb_downloader(
148
146
  silent,
149
147
  ):
150
148
  """Download All-the-Bacteria assemblies based on input query"""
151
- # Setup logs
152
- logging.basicConfig(
153
- format="%(asctime)s:%(name)s:%(levelname)s - %(message)s",
154
- datefmt="%Y-%m-%d %H:%M:%S",
155
- handlers=[
156
- RichHandler(rich_tracebacks=True, console=rich.console.Console(stderr=True))
157
- ],
158
- )
159
- logging.getLogger().setLevel(
160
- logging.ERROR if silent else logging.DEBUG if verbose else logging.INFO
161
- )
149
+ setup_logging(verbose, silent)
162
150
 
163
151
  # If outdir does not exist, create it
164
152
  outdir = mkdir(outdir)
@@ -7,10 +7,10 @@ import rich
7
7
  import rich.console
8
8
  import rich.traceback
9
9
  import rich_click as click
10
- from rich.logging import RichHandler
11
10
 
12
11
  import bactopia
13
12
  from bactopia.atb import create_sample_directory, search_path
13
+ from bactopia.cli.common import common_options, setup_logging
14
14
  from bactopia.utils import validate_file
15
15
 
16
16
  # Set up Rich
@@ -43,7 +43,7 @@ click.rich_click.OPTION_GROUPS = {
43
43
 
44
44
 
45
45
  @click.command()
46
- @click.version_option(bactopia.__version__, "--version", "-V")
46
+ @common_options
47
47
  @click.option(
48
48
  "--path",
49
49
  "-p",
@@ -75,8 +75,6 @@ click.rich_click.OPTION_GROUPS = {
75
75
  @click.option(
76
76
  "--recursive", "-r", is_flag=True, help="Traverse recursively through provided path"
77
77
  )
78
- @click.option("--verbose", is_flag=True, help="Increase the verbosity of output")
79
- @click.option("--silent", is_flag=True, help="Only critical errors will be printed")
80
78
  def atb_formatter(
81
79
  path,
82
80
  bactopia_dir,
@@ -87,17 +85,7 @@ def atb_formatter(
87
85
  silent,
88
86
  ):
89
87
  """Restructure All-the-Bacteria assemblies to allow usage with Bactopia Tools"""
90
- # Setup logs
91
- logging.basicConfig(
92
- format="%(asctime)s:%(name)s:%(levelname)s - %(message)s",
93
- datefmt="%Y-%m-%d %H:%M:%S",
94
- handlers=[
95
- RichHandler(rich_tracebacks=True, console=rich.console.Console(stderr=True))
96
- ],
97
- )
98
- logging.getLogger().setLevel(
99
- logging.ERROR if silent else logging.DEBUG if verbose else logging.INFO
100
- )
88
+ setup_logging(verbose, silent)
101
89
 
102
90
  # Get absolute path of input
103
91
  abspath = validate_file(path)
@@ -11,9 +11,9 @@ import rich
11
11
  import rich.console
12
12
  import rich.traceback
13
13
  import rich_click as click
14
- from rich.logging import RichHandler
15
14
 
16
15
  import bactopia
16
+ from bactopia.cli.common import common_options, setup_logging
17
17
  from bactopia.nf import (
18
18
  find_main_nf,
19
19
  get_bactopia_version,
@@ -29,6 +29,30 @@ stderr = rich.console.Console(stderr=True)
29
29
  rich.traceback.install(console=stderr, width=200, word_wrap=True, extra_lines=1)
30
30
  click.rich_click.USE_RICH_MARKUP = True
31
31
 
32
+ click.rich_click.OPTION_GROUPS = {
33
+ "bactopia-catalog": [
34
+ {"name": "Required Options", "options": ["--bactopia-path"]},
35
+ {
36
+ "name": "Output Options",
37
+ "options": [
38
+ "--output",
39
+ "--pretty",
40
+ "--llms-output",
41
+ "--llms-template",
42
+ ],
43
+ },
44
+ {
45
+ "name": "Additional Options",
46
+ "options": [
47
+ "--verbose",
48
+ "--silent",
49
+ "--version",
50
+ "--help",
51
+ ],
52
+ },
53
+ ]
54
+ }
55
+
32
56
 
33
57
  def _extract_description(groovydoc: dict) -> str:
34
58
  """Extract the first line description from GroovyDoc raw lines."""
@@ -407,7 +431,7 @@ def render_llms_txt(catalog: dict, template_path: Path) -> str:
407
431
 
408
432
 
409
433
  @click.command()
410
- @click.version_option(bactopia.__version__, "--version")
434
+ @common_options
411
435
  @click.option(
412
436
  "--bactopia-path",
413
437
  required=True,
@@ -420,22 +444,21 @@ def render_llms_txt(catalog: dict, template_path: Path) -> str:
420
444
  default=None,
421
445
  help="Output path for catalog.json (default: stdout)",
422
446
  )
423
- @click.option("--pretty", is_flag=True, help="Pretty-print JSON output.")
447
+ @click.option("--pretty", is_flag=True, help="Pretty-print JSON output")
424
448
  @click.option(
425
449
  "--llms-output",
426
450
  "llms_output_path",
427
451
  type=click.Path(),
428
452
  default=None,
429
- help="Also render llms.txt to this path. Uses the bundled template at bactopia/templates/bactopia/llms.txt.j2 unless --llms-template is provided.",
453
+ help="Also render llms.txt to this path. Uses the bundled template at bactopia/templates/bactopia/llms.txt.j2 unless --llms-template is provided",
430
454
  )
431
455
  @click.option(
432
456
  "--llms-template",
433
457
  "llms_template_path",
434
458
  type=click.Path(exists=True, dir_okay=False),
435
459
  default=None,
436
- help="Jinja2 template for llms.txt. Defaults to the template bundled inside bactopia-py.",
460
+ help="Jinja2 template for llms.txt. Defaults to the template bundled inside bactopia-py",
437
461
  )
438
- @click.option("--verbose", is_flag=True, help="Print debug related text.")
439
462
  def catalog(
440
463
  bactopia_path,
441
464
  output_path,
@@ -443,6 +466,7 @@ def catalog(
443
466
  llms_output_path,
444
467
  llms_template_path,
445
468
  verbose,
469
+ silent,
446
470
  ):
447
471
  """Generate machine-readable catalog of all Bactopia components.
448
472
 
@@ -453,15 +477,7 @@ def catalog(
453
477
  Optionally also renders llms.txt from a Jinja2 template when
454
478
  --llms-output is provided.
455
479
  """
456
- # Setup logs
457
- logging.basicConfig(
458
- format="%(asctime)s:%(name)s:%(levelname)s - %(message)s",
459
- datefmt="%Y-%m-%d %H:%M:%S",
460
- handlers=[
461
- RichHandler(rich_tracebacks=True, console=rich.console.Console(stderr=True))
462
- ],
463
- )
464
- logging.getLogger().setLevel(logging.DEBUG if verbose else logging.WARNING)
480
+ setup_logging(verbose, silent)
465
481
 
466
482
  # Validate path
467
483
  bp = Path(bactopia_path).absolute().resolve()
@@ -1,3 +1,5 @@
1
+ """CLI command for printing and validating Bactopia citations."""
2
+
1
3
  import json
2
4
  import sys
3
5
  from pathlib import Path
@@ -11,6 +13,7 @@ from rich.markdown import Markdown
11
13
  from rich.table import Table
12
14
 
13
15
  import bactopia
16
+ from bactopia.cli.common import common_options, setup_logging
14
17
  from bactopia.lint.citations import validate_citations
15
18
  from bactopia.parsers.citations import parse_citations
16
19
  from bactopia.utils import validate_file
@@ -20,6 +23,30 @@ stderr = rich.console.Console(stderr=True)
20
23
  rich.traceback.install(console=stderr, width=200, word_wrap=True, extra_lines=1)
21
24
  click.rich_click.USE_RICH_MARKUP = True
22
25
 
26
+ click.rich_click.OPTION_GROUPS = {
27
+ "bactopia-citations": [
28
+ {"name": "Required Options", "options": ["--bactopia-path"]},
29
+ {
30
+ "name": "Output Options",
31
+ "options": [
32
+ "--name",
33
+ "--plain-text",
34
+ "--validate",
35
+ "--json",
36
+ ],
37
+ },
38
+ {
39
+ "name": "Additional Options",
40
+ "options": [
41
+ "--verbose",
42
+ "--silent",
43
+ "--version",
44
+ "--help",
45
+ ],
46
+ },
47
+ ]
48
+ }
49
+
23
50
 
24
51
  def _render_validation_report(report: dict, silent: bool, plain_text: bool) -> None:
25
52
  """Pretty-print a validate_citations() report using Rich tables."""
@@ -84,7 +111,7 @@ def _render_expected_orphans(console: Console, expected_orphans: dict) -> None:
84
111
 
85
112
 
86
113
  @click.command()
87
- @click.version_option(bactopia.__version__, "--version", "-V")
114
+ @common_options
88
115
  @click.option(
89
116
  "--bactopia-path",
90
117
  "-b",
@@ -104,17 +131,13 @@ def _render_expected_orphans(console: Console, expected_orphans: dict) -> None:
104
131
  is_flag=True,
105
132
  help="Emit validation results as JSON (use with --validate)",
106
133
  )
107
- @click.option(
108
- "--silent",
109
- is_flag=True,
110
- help="Suppress non-error output when validation is clean",
111
- )
112
134
  def citations(
113
135
  bactopia_path: str,
114
136
  name: str,
115
137
  plain_text: bool,
116
138
  validate: bool,
117
139
  as_json: bool,
140
+ verbose: bool,
118
141
  silent: bool,
119
142
  ) -> None:
120
143
  """Print or validate citations used throughout Bactopia.
@@ -125,6 +148,7 @@ def citations(
125
148
  an entry in citations.yml. Module and subworkflow @citation keys are
126
149
  validated by bactopia-lint (rules M035 and S019).
127
150
  """
151
+ setup_logging(verbose, silent)
128
152
 
129
153
  if validate:
130
154
  # Validation mode requires the repo root so we can locate
@@ -0,0 +1,37 @@
1
+ """Shared CLI decorators and helpers for Bactopia commands."""
2
+
3
+ import functools
4
+ import logging
5
+
6
+ import rich.console
7
+ import rich_click as click
8
+ from rich.logging import RichHandler
9
+
10
+ import bactopia
11
+
12
+
13
+ def common_options(fn):
14
+ """Add --verbose, --silent, and --version/-V to a click command."""
15
+
16
+ @click.version_option(bactopia.__version__, "--version", "-V")
17
+ @click.option("--verbose", is_flag=True, help="Print debug related text")
18
+ @click.option("--silent", is_flag=True, help="Only critical errors will be printed")
19
+ @functools.wraps(fn)
20
+ def wrapper(*args, **kwargs):
21
+ return fn(*args, **kwargs)
22
+
23
+ return wrapper
24
+
25
+
26
+ def setup_logging(verbose: bool, silent: bool) -> None:
27
+ """Configure root logger with RichHandler at the appropriate level."""
28
+ logging.basicConfig(
29
+ format="%(asctime)s:%(name)s:%(levelname)s - %(message)s",
30
+ datefmt="%Y-%m-%d %H:%M:%S",
31
+ handlers=[
32
+ RichHandler(rich_tracebacks=True, console=rich.console.Console(stderr=True))
33
+ ],
34
+ )
35
+ logging.getLogger().setLevel(
36
+ logging.ERROR if silent else logging.DEBUG if verbose else logging.INFO
37
+ )
@@ -8,9 +8,9 @@ import rich
8
8
  import rich.console
9
9
  import rich.traceback
10
10
  import rich_click as click
11
- from rich.logging import RichHandler
12
11
 
13
12
  import bactopia
13
+ from bactopia.cli.common import common_options, setup_logging
14
14
  from bactopia.nf import parse_dataset_urls
15
15
  from bactopia.utils import execute
16
16
 
@@ -75,7 +75,7 @@ def download_file(url, save_path, max_retry=5):
75
75
  allow_extra_args=True,
76
76
  )
77
77
  )
78
- @click.version_option(bactopia.__version__, "--version")
78
+ @common_options
79
79
  # Use underscores in parameters and only --, since Nextflow parameters are passed in
80
80
  @click.option(
81
81
  "--bactopia-path",
@@ -91,15 +91,13 @@ def download_file(url, save_path, max_retry=5):
91
91
  @click.option(
92
92
  "--force",
93
93
  is_flag=True,
94
- help="Force overwrite of existing pre-built environments.",
94
+ help="Force overwrite of existing pre-built environments",
95
95
  )
96
96
  @click.option(
97
97
  "--max_retry",
98
98
  default=3,
99
99
  help="Maximum times to attempt creating Conda environment. (Default: 3)",
100
100
  )
101
- @click.option("--verbose", is_flag=True, help="Print debug related text.")
102
- @click.option("--silent", is_flag=True, help="Only critical errors will be printed.")
103
101
  @click.argument("unknown", nargs=-1, type=click.UNPROCESSED)
104
102
  def datasets(
105
103
  bactopia_path,
@@ -111,17 +109,7 @@ def datasets(
111
109
  unknown,
112
110
  ):
113
111
  """Download optional datasets to supplement your analyses with Bactopia"""
114
- # Setup logs
115
- logging.basicConfig(
116
- format="%(asctime)s:%(name)s:%(levelname)s - %(message)s",
117
- datefmt="%Y-%m-%d %H:%M:%S",
118
- handlers=[
119
- RichHandler(rich_tracebacks=True, console=rich.console.Console(stderr=True))
120
- ],
121
- )
122
- logging.getLogger().setLevel(
123
- logging.ERROR if silent else logging.DEBUG if verbose else logging.INFO
124
- )
112
+ setup_logging(verbose, silent)
125
113
 
126
114
  # Install paths
127
115
  bactopia_path = str(Path(bactopia_path).absolute())
@@ -12,6 +12,7 @@ from rich.console import Console
12
12
  from rich.table import Table
13
13
 
14
14
  import bactopia
15
+ from bactopia.cli.common import common_options, setup_logging
15
16
  from bactopia.lint.docs import (
16
17
  DEFAULT_DOCS_PATH,
17
18
  DEFAULT_PATTERNS_FILE,
@@ -22,6 +23,38 @@ stderr = rich.console.Console(stderr=True)
22
23
  rich.traceback.install(console=stderr, width=200, word_wrap=True, extra_lines=1)
23
24
  click.rich_click.USE_RICH_MARKUP = True
24
25
 
26
+ click.rich_click.OPTION_GROUPS = {
27
+ "bactopia-docs": [
28
+ {"name": "Required Options", "options": ["--bactopia-path"]},
29
+ {
30
+ "name": "Validation Options",
31
+ "options": [
32
+ "--docs-path",
33
+ "--patterns-file",
34
+ "--bactopia-py-path",
35
+ "--skip-path-check",
36
+ "--validate",
37
+ ],
38
+ },
39
+ {
40
+ "name": "Output Options",
41
+ "options": [
42
+ "--json",
43
+ "--plain-text",
44
+ ],
45
+ },
46
+ {
47
+ "name": "Additional Options",
48
+ "options": [
49
+ "--verbose",
50
+ "--silent",
51
+ "--version",
52
+ "--help",
53
+ ],
54
+ },
55
+ ]
56
+ }
57
+
25
58
 
26
59
  def _render_report(report: dict, silent: bool, plain_text: bool) -> None:
27
60
  """Pretty-print a validate_docs() report using Rich tables."""
@@ -87,7 +120,7 @@ def _render_report(report: dict, silent: bool, plain_text: bool) -> None:
87
120
 
88
121
 
89
122
  @click.command()
90
- @click.version_option(bactopia.__version__, "--version", "-V")
123
+ @common_options
91
124
  @click.option(
92
125
  "--bactopia-path",
93
126
  "-b",
@@ -111,7 +144,7 @@ def _render_report(report: dict, silent: bool, plain_text: bool) -> None:
111
144
  default=None,
112
145
  type=click.Path(file_okay=False, path_type=Path),
113
146
  help="Path to bactopia-py repo (for D105 CLI / D106 lint-rule checks). "
114
- "Defaults to <bactopia-path>/../bactopia-py.",
147
+ "Defaults to <bactopia-path>/../bactopia-py",
115
148
  )
116
149
  @click.option(
117
150
  "--skip-path-check",
@@ -121,7 +154,7 @@ def _render_report(report: dict, silent: bool, plain_text: bool) -> None:
121
154
  @click.option(
122
155
  "--validate",
123
156
  is_flag=True,
124
- help="Run validation (default action; flag is accepted for parity with bactopia-citations).",
157
+ help="Run validation (default action; flag is accepted for parity with bactopia-citations)",
125
158
  )
126
159
  @click.option(
127
160
  "--json",
@@ -129,11 +162,6 @@ def _render_report(report: dict, silent: bool, plain_text: bool) -> None:
129
162
  is_flag=True,
130
163
  help="Emit results as JSON",
131
164
  )
132
- @click.option(
133
- "--silent",
134
- is_flag=True,
135
- help="Suppress non-error output when validation is clean",
136
- )
137
165
  @click.option(
138
166
  "--plain-text",
139
167
  "-p",
@@ -148,6 +176,7 @@ def docs(
148
176
  skip_path_check: bool,
149
177
  validate: bool,
150
178
  as_json: bool,
179
+ verbose: bool,
151
180
  silent: bool,
152
181
  plain_text: bool,
153
182
  ) -> None:
@@ -168,6 +197,7 @@ def docs(
168
197
 
169
198
  Exits 1 if any FAIL is found.
170
199
  """
200
+ setup_logging(verbose, silent)
171
201
  repo_root = Path(bactopia_path)
172
202
  if not repo_root.is_dir():
173
203
  raise click.ClickException(f"--bactopia-path {repo_root} is not a directory.")
@@ -5,13 +5,15 @@ import time
5
5
  from datetime import datetime
6
6
  from pathlib import Path
7
7
 
8
+ """CLI command for building Bactopia Conda/Docker/Singularity environments."""
9
+
8
10
  import rich
9
11
  import rich.console
10
12
  import rich.traceback
11
13
  import rich_click as click
12
- from rich.logging import RichHandler
13
14
 
14
15
  import bactopia
16
+ from bactopia.cli.common import common_options, setup_logging
15
17
  from bactopia.nf import parse_module_config, parse_workflows
16
18
  from bactopia.utils import execute
17
19
 
@@ -343,7 +345,7 @@ def build_singularity_image(
343
345
  allow_extra_args=True,
344
346
  )
345
347
  )
346
- @click.version_option(bactopia.__version__, "--version")
348
+ @common_options
347
349
  # Use underscores in parameters and only --, since Nextflow parameters are passed in
348
350
  @click.option(
349
351
  "--bactopia-path",
@@ -355,7 +357,7 @@ def build_singularity_image(
355
357
  default="conda",
356
358
  show_default=True,
357
359
  type=click.Choice(["conda", "docker", "singularity", "all"], case_sensitive=False),
358
- help="The type of environment to build.",
360
+ help="The type of environment to build",
359
361
  )
360
362
  @click.option(
361
363
  "--wf",
@@ -383,7 +385,7 @@ def build_singularity_image(
383
385
  "--registry",
384
386
  default="quay.io",
385
387
  show_default=True,
386
- help="Registry to pull Docker containers from.",
388
+ help="Registry to pull Docker containers from",
387
389
  )
388
390
  @click.option(
389
391
  "--singularity_cache",
@@ -399,7 +401,7 @@ def build_singularity_image(
399
401
  @click.option(
400
402
  "--force_rebuild",
401
403
  is_flag=True,
402
- help="Force overwrite of existing pre-built environments.",
404
+ help="Force overwrite of existing pre-built environments",
403
405
  )
404
406
  @click.option(
405
407
  "--max_retry",
@@ -409,10 +411,8 @@ def build_singularity_image(
409
411
  @click.option(
410
412
  "--dry-run",
411
413
  is_flag=True,
412
- help="Show environments that would be built, without building them.",
414
+ help="Show environments that would be built, without building them",
413
415
  )
414
- @click.option("--verbose", is_flag=True, help="Print debug related text.")
415
- @click.option("--silent", is_flag=True, help="Only critical errors will be printed.")
416
416
  @click.argument("unknown", nargs=-1, type=click.UNPROCESSED)
417
417
  def download(
418
418
  bactopia_path,
@@ -432,17 +432,7 @@ def download(
432
432
  unknown,
433
433
  ):
434
434
  """Builds Bactopia environments for use with Nextflow."""
435
- # Setup logs
436
- logging.basicConfig(
437
- format="%(asctime)s:%(name)s:%(levelname)s - %(message)s",
438
- datefmt="%Y-%m-%d %H:%M:%S",
439
- handlers=[
440
- RichHandler(rich_tracebacks=True, console=rich.console.Console(stderr=True))
441
- ],
442
- )
443
- logging.getLogger().setLevel(
444
- logging.ERROR if silent else logging.DEBUG if verbose else logging.INFO
445
- )
435
+ setup_logging(verbose, silent)
446
436
 
447
437
  singularity_exe = "singularity"
448
438
  # Check if -profile x is in unknown
@@ -8,10 +8,10 @@ import rich.console
8
8
  import rich.traceback
9
9
  import rich_click as click
10
10
  from jinja2 import Environment, FileSystemLoader
11
- from rich.logging import RichHandler
12
11
 
13
12
  import bactopia
14
13
  import bactopia.parsers.nextflow as nf_parsers
14
+ from bactopia.cli.common import common_options, setup_logging
15
15
  from bactopia.parsers.generic import parse_json
16
16
  from bactopia.parsers.workflows import get_modules_by_workflow
17
17
 
@@ -75,7 +75,7 @@ click.rich_click.OPTION_GROUPS = {
75
75
  allow_extra_args=True,
76
76
  )
77
77
  )
78
- @click.version_option(bactopia.__version__, "--version")
78
+ @common_options
79
79
  # Use underscores in parameters and only --, since Nextflow parameters are passed in
80
80
  @click.option(
81
81
  "--bactopia-path",
@@ -85,7 +85,7 @@ click.rich_click.OPTION_GROUPS = {
85
85
  @click.option(
86
86
  "--wf",
87
87
  required=True,
88
- help="The workflow to create a nextflow_schema.json for.",
88
+ help="The workflow to create a nextflow_schema.json for",
89
89
  )
90
90
  @click.option(
91
91
  "--outdir",
@@ -94,8 +94,6 @@ click.rich_click.OPTION_GROUPS = {
94
94
  help="Directory to write output files to",
95
95
  )
96
96
  @click.option("--force", is_flag=True, help="Overwrite existing output files")
97
- @click.option("--verbose", is_flag=True, help="Print debug related text.")
98
- @click.option("--silent", is_flag=True, help="Only critical errors will be printed.")
99
97
  @click.argument("unknown", nargs=-1, type=click.UNPROCESSED)
100
98
  def merge_schemas(
101
99
  bactopia_path,
@@ -107,17 +105,7 @@ def merge_schemas(
107
105
  unknown,
108
106
  ):
109
107
  """Builds a Nextflow Schema and/or Nextflow config for a given workflow."""
110
- # Setup logs
111
- logging.basicConfig(
112
- format="%(asctime)s:%(name)s:%(levelname)s - %(message)s",
113
- datefmt="%Y-%m-%d %H:%M:%S",
114
- handlers=[
115
- RichHandler(rich_tracebacks=True, console=rich.console.Console(stderr=True))
116
- ],
117
- )
118
- logging.getLogger().setLevel(
119
- logging.ERROR if silent else logging.DEBUG if verbose else logging.INFO
120
- )
108
+ setup_logging(verbose, silent)
121
109
 
122
110
  # bactopia paths
123
111
  bactopia_path = str(Path(bactopia_path).absolute())
@@ -9,9 +9,9 @@ import rich
9
9
  import rich.console
10
10
  import rich.traceback
11
11
  import rich_click as click
12
- from rich.logging import RichHandler
13
12
 
14
13
  import bactopia
14
+ from bactopia.cli.common import common_options, setup_logging
15
15
  from bactopia.lint.runner import run_lint
16
16
 
17
17
  # Set up Rich
@@ -44,6 +44,7 @@ click.rich_click.OPTION_GROUPS = {
44
44
  "name": "Additional Options",
45
45
  "options": [
46
46
  "--verbose",
47
+ "--silent",
47
48
  "--version",
48
49
  "--help",
49
50
  ],
@@ -117,7 +118,7 @@ def print_rich(
117
118
 
118
119
 
119
120
  @click.command()
120
- @click.version_option(bactopia.__version__, "--version")
121
+ @common_options
121
122
  @click.option(
122
123
  "--bactopia-path",
123
124
  required=True,
@@ -160,13 +161,12 @@ def print_rich(
160
161
  "-q",
161
162
  "--quiet",
162
163
  is_flag=True,
163
- help="Only show components with warnings or failures.",
164
+ help="Only show components with warnings or failures",
164
165
  )
165
- @click.option("--json", "use_json", is_flag=True, help="Output as JSON.")
166
+ @click.option("--json", "use_json", is_flag=True, help="Output as JSON")
166
167
  @click.option(
167
- "--pretty", is_flag=True, help="Pretty-print JSON output (implies --json)."
168
+ "--pretty", is_flag=True, help="Pretty-print JSON output (implies --json)"
168
169
  )
169
- @click.option("--verbose", is_flag=True, help="Print debug related text.")
170
170
  def lint(
171
171
  bactopia_path,
172
172
  modules,
@@ -179,21 +179,14 @@ def lint(
179
179
  use_json,
180
180
  pretty,
181
181
  verbose,
182
+ silent,
182
183
  ):
183
184
  """Lint Bactopia pipeline components against style guidelines.
184
185
 
185
186
  Checks modules, subworkflows, and workflows for compliance with
186
187
  Bactopia's GroovyDoc, structural, and configuration standards.
187
188
  """
188
- # Setup logs
189
- logging.basicConfig(
190
- format="%(asctime)s:%(name)s:%(levelname)s - %(message)s",
191
- datefmt="%Y-%m-%d %H:%M:%S",
192
- handlers=[
193
- RichHandler(rich_tracebacks=True, console=rich.console.Console(stderr=True))
194
- ],
195
- )
196
- logging.getLogger().setLevel(logging.DEBUG if verbose else logging.WARNING)
189
+ setup_logging(verbose, silent)
197
190
 
198
191
  # Validate path
199
192
  bp = Path(bactopia_path).absolute().resolve()