1+ # !/usr/bin/env pwsh
2+ # Script to diagnose environment differences that might cause CI flakiness
3+ # Usage: .\diagnose-environment.ps1
4+
5+ Write-Host " Diagnosing Windows environment for stdio test issues..." - ForegroundColor Cyan
6+ Write-Host " "
7+
8+ # System Information
9+ Write-Host " === SYSTEM INFORMATION ===" - ForegroundColor Yellow
10+ Write-Host " Windows Version:"
11+ (Get-CimInstance Win32_OperatingSystem).Version
12+ Write-Host " Windows Build:"
13+ (Get-ItemProperty " HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" ).DisplayVersion
14+ Write-Host " "
15+
16+ # Python Information
17+ Write-Host " === PYTHON INFORMATION ===" - ForegroundColor Yellow
18+ Write-Host " Python Version:"
19+ python -- version
20+ Write-Host " "
21+ Write-Host " Python Build Info:"
22+ python - c " import sys; print(f'Version: {sys.version}')"
23+ python - c " import sys; print(f'Platform: {sys.platform}')"
24+ python - c " import sys; print(f'Windows Version: {sys.getwindowsversion()}')"
25+ Write-Host " "
26+
27+ # Check subprocess configuration
28+ Write-Host " === SUBPROCESS CONFIGURATION ===" - ForegroundColor Yellow
29+ python - c @"
30+ import subprocess
31+ import sys
32+ print(f'CREATE_NO_WINDOW available: {hasattr(subprocess, "CREATE_NO_WINDOW")}')
33+ print(f'CREATE_NEW_PROCESS_GROUP available: {hasattr(subprocess, "CREATE_NEW_PROCESS_GROUP")}')
34+ print(f'Windows subprocess startup info: {hasattr(subprocess, "STARTUPINFO")}')
35+
36+ # Check handle inheritance defaults
37+ import os
38+ print(f'\nHandle inheritance (os.O_NOINHERIT): {hasattr(os, "O_NOINHERIT")}')
39+
40+ # Check asyncio event loop
41+ import asyncio
42+ try:
43+ loop = asyncio.get_event_loop_policy()
44+ print(f'Event loop policy: {type(loop).__name__}')
45+ except:
46+ print('Event loop policy: Unable to determine')
47+ "@
48+ Write-Host " "
49+
50+ # Check for Job Objects support
51+ Write-Host " === JOB OBJECTS SUPPORT ===" - ForegroundColor Yellow
52+ python - c @"
53+ try:
54+ import win32job
55+ import win32api
56+ print('pywin32 available: Yes')
57+ print(f'win32job version: {win32job.__file__}')
58+
59+ # Try to create a job object
60+ try:
61+ job = win32job.CreateJobObject(None, '')
62+ win32api.CloseHandle(job)
63+ print('Job Object creation: Success')
64+ except Exception as e:
65+ print(f'Job Object creation: Failed - {e}')
66+ except ImportError:
67+ print('pywin32 available: No (Job Objects not available)')
68+ "@
69+ Write-Host " "
70+
71+ # Check process limits
72+ Write-Host " === PROCESS LIMITS ===" - ForegroundColor Yellow
73+ python - c @"
74+ import os
75+ import psutil
76+ proc = psutil.Process(os.getpid())
77+ print(f'Open handles: {proc.num_handles()}')
78+ print(f'Open files: {len(proc.open_files())}')
79+
80+ # Check system-wide limits
81+ print(f'Total processes: {len(psutil.pids())}')
82+ "@
83+ Write-Host " "
84+
85+ # Check security software
86+ Write-Host " === SECURITY SOFTWARE ===" - ForegroundColor Yellow
87+ Get-CimInstance - Namespace " root\SecurityCenter2" - ClassName AntiVirusProduct - ErrorAction SilentlyContinue |
88+ Select-Object displayName, productState | Format-Table
89+ Write-Host " "
90+
91+ # Test rapid process creation
92+ Write-Host " === RAPID PROCESS CREATION TEST ===" - ForegroundColor Yellow
93+ Write-Host " Testing rapid tee process creation/destruction..."
94+ $testScript = @'
95+ import time
96+ import subprocess
97+ import sys
98+
99+ failures = 0
100+ times = []
101+
102+ for i in range(20):
103+ start = time.time()
104+ try:
105+ # Create process with same flags as stdio client
106+ proc = subprocess.Popen(
107+ ['tee'],
108+ stdin=subprocess.PIPE,
109+ stdout=subprocess.PIPE,
110+ stderr=subprocess.PIPE,
111+ creationflags=getattr(subprocess, 'CREATE_NO_WINDOW', 0)
112+ )
113+ proc.stdin.close()
114+ proc.wait(timeout=0.5)
115+ elapsed = time.time() - start
116+ times.append(elapsed)
117+ except Exception as e:
118+ failures += 1
119+ print(f" Iteration {i+1}: FAILED - {e}")
120+
121+ if (i+1) % 5 == 0:
122+ avg_time = sum(times) / len(times) if times else 0
123+ print(f" Completed {i+1}/20 (avg: {avg_time*1000:.1f}ms)")
124+
125+ print(f"\nFailures: {failures}/20")
126+ if times:
127+ print(f"Average time: {sum(times)/len(times)*1000:.1f}ms")
128+ print(f"Max time: {max(times)*1000:.1f}ms")
129+ '@
130+
131+ python - c $testScript
132+ Write-Host " "
133+
134+ # Environment variables that might affect subprocess
135+ Write-Host " === RELEVANT ENVIRONMENT VARIABLES ===" - ForegroundColor Yellow
136+ @ (" COMSPEC" , " PATH" , " PYTHONPATH" , " PYTHONASYNCIODEBUG" ) | ForEach-Object {
137+ $value = [Environment ]::GetEnvironmentVariable($_ )
138+ if ($value ) {
139+ Write-Host " $_ `:"
140+ Write-Host " $value " - ForegroundColor Gray
141+ }
142+ }
143+ Write-Host " "
144+
145+ Write-Host " === DIAGNOSIS COMPLETE ===" - ForegroundColor Cyan
146+ Write-Host " "
147+ Write-Host " Share this output when reporting the flakiness issue." - ForegroundColor Yellow
148+ Write-Host " Key differences to look for between local and CI:" - ForegroundColor Yellow
149+ Write-Host " - Windows version/build" - ForegroundColor Gray
150+ Write-Host " - Python build details" - ForegroundColor Gray
151+ Write-Host " - Security software presence" - ForegroundColor Gray
152+ Write-Host " - Process creation timing" - ForegroundColor Gray
0 commit comments