2323manage the run and download results after completion.
2424"""
2525
26+ import os .path
2627from inspect import cleandoc
2728from shlex import quote as shquote
2829from textwrap import dedent
2930from .. import runner
3031from ..argparse import add_extended_help_flags , MkDirectoryPath , SKIP_AUTO_DEFAULT_IN_HELP
31- from ..debug import DEBUGGING
32+ from ..debug import DEBUGGING , debug
3233from ..errors import UserError
33- from ..pathogens import PathogenVersion
34+ from ..pathogens import PathogenVersion , UnmanagedPathogen
3435from ..runner import aws_batch , docker , singularity
3536from ..util import byte_quantity , split_image_name
3637from ..volume import NamedVolume
3940
4041def register_parser (subparser ):
4142 """
42- %(prog)s [options] <pathogen-name>[@<version>] <workflow-name> <analysis-directory> [<target> [<target> [...]]]
43+ %(prog)s [options] <pathogen-name>[@<version>]|<pathogen-path> <workflow-name> <analysis-directory> [<target> [<target> [...]]]
4344 %(prog)s --help
4445 """
4546
@@ -48,14 +49,19 @@ def register_parser(subparser):
4849 # Positional parameters
4950 parser .add_argument (
5051 "pathogen" ,
51- metavar = "<pathogen-name>[@<version>]" ,
52+ metavar = "<pathogen-name>[@<version>]|<pathogen-path> " ,
5253 help = cleandoc (f"""
5354 The name (and optionally, version) of a previously set up pathogen.
5455 See :command-reference:`nextstrain setup`. If no version is
5556 specified, then the default version (if any) will be used.
5657
58+ Alternatively, the local path to a directory that is a pathogen
59+ repository. For this case to be recognized as such, the path must
60+ contain a separator ({{path_sep}}) or consist entirely of the current
61+ directory ({ os .path .curdir } ) or parent directory ({ os .path .pardir } ) specifier.
62+
5763 Required.
58- """ ))
64+ """ . format ( path_sep = " or " . join ( sorted ( set ([ os . path . sep , os . path . altsep or os . path . sep ])))) ))
5965
6066 parser .add_argument (
6167 "workflow" ,
@@ -226,21 +232,32 @@ def run(opts):
226232 """ )
227233
228234 # Resolve pathogen and workflow names to a local workflow directory.
229- pathogen = PathogenVersion (opts .pathogen )
235+ try :
236+ pathogen = UnmanagedPathogen (opts .pathogen )
237+ except ValueError :
238+ debug (f"Treating { opts .pathogen !r} as managed pathogen version" )
239+ pathogen = PathogenVersion (opts .pathogen )
240+ else :
241+ debug (f"Treating { opts .pathogen !r} as unmanaged pathogen directory" )
230242
231243 if opts .workflow not in pathogen .registered_workflows ():
232244 print (f"The { opts .workflow !r} workflow is not registered as a compatible workflow, but trying to run anyways." )
233245
234246 workflow_directory = pathogen .workflow_path (opts .workflow )
235247
236248 if not workflow_directory .is_dir () or not (workflow_directory / "Snakefile" ).is_file ():
237- raise UserError (f"""
238- No { opts .workflow !r} workflow for pathogen { opts .pathogen !r} found { f"in { str (workflow_directory )!r} " if DEBUGGING else "locally" } .
249+ if isinstance (pathogen , UnmanagedPathogen ):
250+ raise UserError (f"""
251+ No { opts .workflow !r} workflow for pathogen { opts .pathogen !r} found { f"in { str (workflow_directory )!r} " if DEBUGGING else "locally" } .
252+ """ )
253+ else :
254+ raise UserError (f"""
255+ No { opts .workflow !r} workflow for pathogen { opts .pathogen !r} found { f"in { str (workflow_directory )!r} " if DEBUGGING else "locally" } .
239256
240- Maybe you need to update to a newer version of the pathogen?
257+ Maybe you need to update to a newer version of the pathogen?
241258
242- Hint: to update the pathogen, run `nextstrain update { shquote (pathogen .name )} `.
243- """ )
259+ Hint: to update the pathogen, run `nextstrain update { shquote (pathogen .name )} `.
260+ """ )
244261
245262 # The pathogen volume is the pathogen directory (i.e. repo).
246263 # The workflow volume is the workflow directory within the pathogen directory.
@@ -274,6 +291,14 @@ def run(opts):
274291 * (["--forceall" ]
275292 if opts .force else []),
276293
294+ # Explicitly use Snakemake's current working directory as the
295+ # workflow's workdir, overriding any "workdir:" directive the workflow
296+ # may include. Snakemake uses the cwd by default in the absence of any
297+ # "workdir:" directive, but we want to _always_ use it to avoid writing
298+ # into the pathogen/workflow source directories if a non-compatible
299+ # workflow is run.
300+ "--directory=." ,
301+
277302 # Workdir will be the analysis volume (/nextstrain/build in a
278303 # containerized runtime), so explicitly point to the Snakefile.
279304 "--snakefile=%s/Snakefile" % (
0 commit comments