3535import nox
3636
3737nox .options .reuse_existing_virtualenvs = True
38+ nox .options .error_on_missing_interpreters = True
3839
3940REPO_ROOT = Path (__file__ ).parent
4041AZUREMANAGED = REPO_ROOT / "durabletask-azuremanaged"
@@ -118,6 +119,21 @@ def _wait_for_ports(ports: Sequence[int], timeout: int = 30) -> bool:
118119 return False
119120
120121
122+ def _pytest_arguments (
123+ default_target : str ,
124+ posargs : Sequence [str ],
125+ ) -> tuple [str , ...]:
126+ """Keep the default target when positional arguments are only pytest options."""
127+ if not posargs :
128+ return (default_target ,)
129+
130+ has_target = any (
131+ "::" in argument or Path (argument ).exists ()
132+ for argument in posargs
133+ )
134+ return tuple (posargs ) if has_target else (default_target , * posargs )
135+
136+
121137def _start_azurite (
122138 session : nox .Session ,
123139 ports : Sequence [int ],
@@ -162,18 +178,32 @@ def _start_azurite(
162178 session .log ("Started Azurite for this session." )
163179 return process
164180
165- if process .poll () is None :
166- process .terminate ()
167- process .wait ()
181+ _stop_process (process )
168182 session .error ("Azurite did not become ready within 30 seconds." )
169183 return None
170184
171185
172186def _stop_process (process : subprocess .Popen [bytes ] | None ) -> None :
173187 """Stop a service process that this Nox session started."""
174- if process is not None and process .poll () is None :
188+ if process is None :
189+ return
190+
191+ if os .name == "nt" :
192+ script = f"""
193+ function Stop-ProcessTree([int] $processId) {{
194+ Get-CimInstance Win32_Process -Filter "ParentProcessId = $processId" |
195+ ForEach-Object {{ Stop-ProcessTree $_.ProcessId }}
196+ Stop-Process -Id $processId -Force -ErrorAction SilentlyContinue
197+ }}
198+ Stop-ProcessTree { process .pid }
199+ """
200+ subprocess .run (
201+ ["powershell" , "-NoProfile" , "-Command" , script ],
202+ check = True ,
203+ )
204+ elif process .poll () is None :
175205 process .terminate ()
176- process .wait ()
206+ process .wait ()
177207
178208
179209def _new_test_namespace (prefix : str ) -> str :
@@ -304,10 +334,10 @@ def core_tests(session: nox.Session) -> None:
304334 try :
305335 session .install ("-r" , "requirements.txt" )
306336 session .install ("-e" , f"{ REPO_ROOT } [azure-blob-payloads]" , "aiohttp" )
307- targets = session . posargs or ("tests/durabletask" ,)
337+ arguments = _pytest_arguments ("tests/durabletask" , session . posargs )
308338 session .run (
309339 "pytest" ,
310- * targets ,
340+ * arguments ,
311341 "-m" ,
312342 "not dts" ,
313343 "--verbose" ,
@@ -331,8 +361,11 @@ def azuremanaged_tests(session: nox.Session) -> None:
331361 "-e" ,
332362 str (AZUREMANAGED ),
333363 )
334- targets = session .posargs or ("tests/durabletask-azuremanaged" ,)
335- session .run ("pytest" , * targets , "-m" , "dts" , "--verbose" )
364+ arguments = _pytest_arguments (
365+ "tests/durabletask-azuremanaged" ,
366+ session .posargs ,
367+ )
368+ session .run ("pytest" , * arguments , "-m" , "dts" , "--verbose" )
336369 finally :
337370 _stop_dts_emulator (container_name )
338371
@@ -343,9 +376,9 @@ def functions_unit(session: nox.Session) -> None:
343376 session .install ("-r" , "requirements.txt" )
344377 _install_packages (session , editable = True )
345378 session .install ("pytest" )
346- targets = session . posargs or ("tests/azure-functions-durable" ,)
379+ arguments = _pytest_arguments ("tests/azure-functions-durable" , session . posargs )
347380 session .run (
348- "pytest" , * targets ,
381+ "pytest" , * arguments ,
349382 "-m" , "not dts and not azurite and not functions_e2e" ,
350383 )
351384
@@ -391,9 +424,12 @@ def functions_e2e(session: nox.Session) -> None:
391424 session .install ("pytest" )
392425 for app in E2E_APPS :
393426 _link_app_venv (session , E2E_APPS_DIR / app )
394- targets = session .posargs or ("tests/azure-functions-durable/e2e" ,)
427+ arguments = _pytest_arguments (
428+ "tests/azure-functions-durable/e2e" ,
429+ session .posargs ,
430+ )
395431 session .run (
396- "pytest" , * targets ,
432+ "pytest" , * arguments ,
397433 "-m" , "functions_e2e" ,
398434 )
399435 finally :
@@ -419,10 +455,10 @@ def ci(session: nox.Session) -> None:
419455 f"{ ', ' .join (PYTHON_VERSIONS )} ." )
420456
421457 python_version = session .posargs [0 ] if session .posargs else DEFAULT_CI_PYTHON
422- session .notify ("lint" )
423- session .notify ("typecheck_core" )
424- session .notify ("typecheck_functions" )
425- session .notify (f"core_tests-{ python_version } " )
426- session .notify (f"azuremanaged_tests-{ python_version } " )
427- session .notify ("functions_unit-3.13" )
428- session .notify ("functions_e2e" )
458+ session .notify ("lint" , () )
459+ session .notify ("typecheck_core" , () )
460+ session .notify ("typecheck_functions" , () )
461+ session .notify (f"core_tests-{ python_version } " , () )
462+ session .notify (f"azuremanaged_tests-{ python_version } " , () )
463+ session .notify ("functions_unit-3.13" , () )
464+ session .notify ("functions_e2e" , () )
0 commit comments