Skip to content

Commit d40730d

Browse files
Merge pull request #23 from hsaunders1904/make_pwsh_activator_more_robust
Make pwsh activator more robust
2 parents 698c850 + 567653a commit d40730d

File tree

4 files changed

+62
-34
lines changed

4 files changed

+62
-34
lines changed

PyAutoEnv.ps1

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,7 @@
1313
#
1414
# You should have received a copy of the GNU General Public License
1515
# along with this program. If not, see <https://www.gnu.org/licenses/>.
16-
$PyAutoEnvDir = "${PSScriptRoot}"
17-
18-
if (Test-Path alias:cd) {
19-
Remove-Alias -Name cd -Force -Scope Global
20-
}
16+
$pyAutoEnvDir = "${PSScriptRoot}"
2117

2218
<#
2319
.SYNOPSIS
@@ -35,12 +31,12 @@ function Invoke-PyAutoEnv() {
3531
if (-Not (Get-Command "python" -ErrorAction SilentlyContinue)) {
3632
return
3733
}
38-
$PyAutoEnv = Join-Path "${PyAutoEnvDir}" "pyautoenv.py"
39-
if (Test-Path "${PyAutoEnv}") {
40-
$Expression = "$(python "${PyAutoEnv}" --pwsh)"
41-
if (${Expression}) {
42-
Invoke-Expression "${Expression}"
43-
}
34+
$pyAutoEnv = Join-Path "${pyAutoEnvDir}" "pyautoenv.py"
35+
if (Test-Path "${pyAutoEnv}") {
36+
$expression = "$(python "${pyAutoEnv}" --pwsh)"
37+
if (${expression}) {
38+
Invoke-Expression "${expression}"
39+
}
4440
}
4541
}
4642

@@ -51,12 +47,46 @@ function Invoke-PyAutoEnv() {
5147
https://github.com/hsaunders1904/pyautoenv/
5248
#>
5349
function Invoke-PyAutoEnvVersion() {
54-
$PyAutoEnv = Join-Path "${PyAutoEnvDir}" "pyautoenv.py"
55-
python "${PyAutoEnv}" --version
50+
$pyAutoEnv = Join-Path "${pyAutoEnvDir}" "pyautoenv.py"
51+
python "${pyAutoEnv}" --version
5652
}
5753

58-
function cd() {
59-
Set-Location @Args && Invoke-PyAutoEnv
54+
<#
55+
.SYNOPSIS
56+
Create a proxy function definition for a Cmdlet that executes pyautoenv.
57+
.LINK
58+
https://github.com/hsaunders1904/pyautoenv/
59+
#>
60+
function New-PyAutoEnvProxyFunctionDefinition([string] $commandString)
61+
{
62+
# Generate base code for the Proxy function.
63+
$originalCommand = Get-Command -Name "$commandString" -CommandType Cmdlet
64+
$metaData = New-Object System.Management.Automation.CommandMetaData $originalCommand
65+
$proxyCode = [System.Management.Automation.ProxyCommand]::Create($metaData)
66+
67+
# Find the 'end' block of Set-Location's source.
68+
$ast = [System.Management.Automation.Language.Parser]::ParseInput($proxyCode, [ref]$null, [ref]$null)
69+
$endBlock = $ast.EndBlock.Extent.Text
70+
$endBlockClosingIndex = $endBlock.LastIndexOf('}')
71+
if ($endBlockClosingIndex -Le 0) {
72+
# If we can't find the opening brace, something's not right, so exit early
73+
# without editing the proxy to avoid breaking things.
74+
$body = $ast.ToString()
75+
return "function $commandString {`n${body}`n}"
76+
}
77+
78+
# Insert the pyautoenv function call into the 'end' block of the proxy code.
79+
$tab = " "
80+
$insert = "`n${tab}try {`n${tab}${tab}Invoke-PyAutoEnv`n${tab}} catch {}`n"
81+
$newEndBlockOpen = $endBlock.Substring(0, $endBlockClosingIndex) + $insert
82+
$newEndBlock = $newEndBlockOpen + $endBlock.Substring($endBlockClosingIndex)
83+
$updatedProxyCmd = $proxyCode.Replace($endBlock, $newEndBlock)
84+
return "function global:$commandString {`n$updatedProxyCmd`n}"
6085
}
6186

62-
Invoke-PyAutoEnv
87+
foreach ($commandName in ("Set-Location", "Push-Location", "Pop-Location")) {
88+
Invoke-Expression (& {
89+
(New-PyAutoEnvProxyFunctionDefinition "$commandName" | Out-String)
90+
})
91+
}
92+
Invoke-PyAutoEnv # Look for environment in initial directory.

README.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,6 @@ To enable the application in PowerShell, dot the `.ps1` file.
9595

9696
Add this to your profile to activate the application permanently.
9797

98-
Note that this application hooks into and re-aliases `cd`.
99-
Therefore `pyautoenv` does not support the use of `Set-Location`.
100-
10198
</details>
10299

103100
## Options

pyautoenv.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -373,26 +373,27 @@ def poetry_project_name(directory: str) -> Union[str, None]:
373373

374374
def activator(env_directory: str, args: Args) -> str:
375375
"""Get the activator script for the environment in the given directory."""
376-
dir_name = "Scripts" if operating_system() == Os.WINDOWS else "bin"
376+
is_windows = operating_system() == Os.WINDOWS
377+
dir_name = "Scripts" if is_windows else "bin"
377378
if args.fish:
378379
script = "activate.fish"
379380
elif args.pwsh:
380-
poetry_dir = poetry_cache_dir()
381-
if (
382-
poetry_dir is not None
383-
and env_directory.startswith(poetry_dir)
384-
and operating_system() != Os.WINDOWS
385-
):
386-
# In poetry environments on *NIX systems, this activator has
387-
# a lowercase A.
388-
script = "activate.ps1"
381+
if is_windows:
382+
script = "Activate.ps1"
389383
else:
390-
# In venv environments, and Windows poetry environments,
391-
# this activator has an uppercase A.
384+
# PowerShell activation scripts on Nix systems have some
385+
# slightly inconsistent naming. When using Poetry or uv, the
386+
# activation script is lower case, using the venv module,
387+
# the script is title case.
388+
# We can't really know what was used to generate the venv
389+
# so just check which activation script exists.
390+
script_path = os.path.join(env_directory, dir_name, "activate.ps1")
391+
if os.path.isfile(script_path):
392+
return script_path
392393
script = "Activate.ps1"
393394
else:
394395
script = "activate"
395-
return os.path.join(env_directory, dir_name, f"{script}")
396+
return os.path.join(env_directory, dir_name, script)
396397

397398

398399
@lru_cache(maxsize=128)

tests/test_venv.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def fs(self, fs: FakeFilesystem) -> FakeFilesystem:
6363
fs.create_dir(self.PY_PROJ / "src")
6464
fs.create_file(self.VENV_DIR / "bin" / "activate")
6565
fs.create_file(self.VENV_DIR / "bin" / "activate.fish")
66-
fs.create_file(self.VENV_DIR / "bin" / "Activate.ps1")
66+
fs.create_file(self.VENV_DIR / "bin" / "activate.ps1")
6767
fs.create_file(self.VENV_DIR / "Scripts" / "activate")
6868
fs.create_file(self.VENV_DIR / "Scripts" / "Activate.ps1")
6969
fs.create_dir("not_a_venv")
@@ -200,7 +200,7 @@ class TestVenvBashLinux(VenvTester):
200200

201201

202202
class TestVenvPwshLinux(VenvTester):
203-
activator = "bin/Activate.ps1"
203+
activator = "bin/activate.ps1"
204204
flag = "--pwsh"
205205
os = pyautoenv.Os.LINUX
206206

0 commit comments

Comments
 (0)