-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathInstall-PythonFileLauncher.ps1
More file actions
126 lines (118 loc) · 5.46 KB
/
Copy pathInstall-PythonFileLauncher.ps1
File metadata and controls
126 lines (118 loc) · 5.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
<#
.SYNOPSIS
Install or remove "Run Python with arguments..." for every .py/.pyw file.
.DESCRIPTION
Registers a current-user static shell verb under SystemFileAssociations, so
it survives changes to the default editor. No administrator rights required.
#>
[CmdletBinding()]
param([switch]$Uninstall)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$launcher = Join-Path $PSScriptRoot 'python_arg_launcher.pyw'
$pyguard = Join-Path $PSScriptRoot 'pyguard.py'
$guardianPythonw = Join-Path $PSScriptRoot 'guardian_venv\Scripts\pythonw.exe'
$pythonw = $guardianPythonw
if (-not $Uninstall -and -not (Test-Path -LiteralPath $pythonw -PathType Leaf)) {
throw 'The Guardian runtime has not been installed.'
}
$shimRoot = $PSScriptRoot
foreach ($shimName in 'PyGuard.cmd', 'pyguardian.cmd') {
$shimPath = Join-Path $shimRoot $shimName
if ($Uninstall) {
if (Test-Path -LiteralPath $shimPath -PathType Leaf) {
$content = Get-Content -Raw -LiteralPath $shimPath
if ($content -match '(?m)^:: Python Guardian 0\.1\.0\s*$') {
Remove-Item -LiteralPath $shimPath -Force
}
}
continue
}
New-Item -ItemType Directory -Path $shimRoot -Force | Out-Null
if (Test-Path -LiteralPath $shimPath -PathType Leaf) {
$existing = Get-Content -Raw -LiteralPath $shimPath
if ($existing -notmatch '(?m)^:: Python Guardian 0\.1\.0\s*$') {
throw "Command shim already exists and was not created by Guardian: $shimPath"
}
}
$shim = "@echo off`r`n:: Python Guardian 0.1.0`r`n`"$guardianPythonw`" `"$pyguard`" %*`r`n"
# Use console Python so CLI output remains visible.
$shim = $shim.Replace('pythonw.exe', 'python.exe')
Set-Content -LiteralPath $shimPath -Value $shim -Encoding ASCII -NoNewline
}
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
$pathParts = @($userPath -split ';' | Where-Object { $_ })
if ($Uninstall) {
$pathParts = @($pathParts | Where-Object {
-not [string]::Equals($_.TrimEnd('\'), $PSScriptRoot.TrimEnd('\'),
[System.StringComparison]::OrdinalIgnoreCase)
})
} elseif (-not ($pathParts | Where-Object {
[string]::Equals($_.TrimEnd('\'), $PSScriptRoot.TrimEnd('\'),
[System.StringComparison]::OrdinalIgnoreCase)
})) {
$pathParts += $PSScriptRoot
}
[Environment]::SetEnvironmentVariable('Path', ($pathParts -join ';'), 'User')
# Remove shims from the experimental WindowsApps location used by pre-release builds.
$legacyRoot = Join-Path $env:LOCALAPPDATA 'Microsoft\WindowsApps'
foreach ($shimName in 'PyGuard.cmd', 'pyguardian.cmd') {
$legacy = Join-Path $legacyRoot $shimName
if (Test-Path -LiteralPath $legacy -PathType Leaf) {
$legacyContent = Get-Content -Raw -LiteralPath $legacy
if ($legacyContent -match '(?m)^:: Python Guardian 0\.1\.0\s*$') {
Remove-Item -LiteralPath $legacy -Force
}
}
}
if (-not (Test-Path -LiteralPath $launcher -PathType Leaf)) { throw "Missing launcher: $launcher" }
if (-not $Uninstall -and -not (Test-Path -LiteralPath $guardianPythonw -PathType Leaf)) {
throw "Guardian runtime is missing. Run: $PSScriptRoot\Install-PythonGuardianRuntime.ps1"
}
$verbs = @(
@{ Name = 'PythonArgLauncher'; Label = 'Run Python with arguments…'; Arguments = '"%1"' },
@{ Name = 'PythonGuardian'; Label = 'Run under Codex supervision…'; Arguments = '--supervised "%1"' }
)
foreach ($extension in '.py', '.pyw') {
foreach ($definition in $verbs) {
$verb = "HKCU:\Software\Classes\SystemFileAssociations\$extension\shell\$($definition.Name)"
if ($Uninstall) {
Remove-Item -LiteralPath $verb -Recurse -Force -ErrorAction SilentlyContinue
continue
}
New-Item -Path $verb -Force | Out-Null
Set-Item -LiteralPath $verb -Value $definition.Label
New-ItemProperty -LiteralPath $verb -Name 'Icon' -Value "$pythonw,0" -PropertyType String -Force | Out-Null
$commandKey = Join-Path $verb 'command'
New-Item -Path $commandKey -Force | Out-Null
Set-Item -LiteralPath $commandKey -Value "`"$pythonw`" `"$launcher`" $($definition.Arguments)"
}
}
if (-not ('PythonGuardianShellRefresh' -as [type])) {
Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
public static class PythonGuardianShellRefresh {
[DllImport("shell32.dll")]
public static extern void SHChangeNotify(uint eventId, uint flags, IntPtr item1, IntPtr item2);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint msg, UIntPtr wParam,
string lParam, uint flags, uint timeout, out UIntPtr result);
}
'@
}
[PythonGuardianShellRefresh]::SHChangeNotify(0x08000000, 0, [IntPtr]::Zero, [IntPtr]::Zero)
$broadcastResult = [UIntPtr]::Zero
[void][PythonGuardianShellRefresh]::SendMessageTimeout(
[IntPtr]0xffff, 0x001A, [UIntPtr]::Zero, 'Environment', 2, 5000, [ref]$broadcastResult
)
if ($Uninstall) {
Write-Output 'Removed Python File Launcher and Python Guardian from .py and .pyw context menus.'
} else {
Write-Output 'Installed both Python launcher verbs for .py and .pyw files.'
Write-Output ' Run Python with arguments…'
Write-Output ' Run under Codex supervision…'
Write-Output "Launcher: $launcher"
Write-Output 'Windows 11 location: right-click -> Show more options.'
Write-Output 'PowerShell command: PyGuard your_script.py'
}