Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ History
master
^^^^^^

- Quote Fish PATH entries so locations with spaces (e.g. ``Application Support``) are a single token

1.9.2
^^^^^

Expand Down
20 changes: 20 additions & 0 deletions tests/test_fish.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,23 @@ def test_append_multiple(self, request, shell_test):
stdout, stderr = process.communicate()

assert process.returncode == 0, (stdout + stderr).decode('utf-8')


def test_fish_config_quotes_paths_with_spaces():
from os import pathsep

from userpath.shells import Fish

fish = Fish(home='/tmp/home')
location = '/Users/hynek/Library/Application Support/hatch/bin'
contents = next(iter(fish.config(location, front=True).values()))
assert contents == 'set PATH "{}" $PATH'.format(location)

contents = next(iter(fish.config(location, front=False).values()))
assert contents == 'set PATH $PATH "{}"'.format(location)

locations = pathsep.join(
['/foo/Application Support/bin', '/bar/My Apps/bin']
)
contents = next(iter(fish.config(locations, front=True).values()))
assert contents == 'set PATH "/foo/Application Support/bin" "/bar/My Apps/bin" $PATH'
16 changes: 14 additions & 2 deletions userpath/shells.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,22 @@ def show_path_commands(cls):
return [['bash', '-i', '-c', 'echo $PATH'], ['bash', '-i', '-l', '-c', 'echo $PATH']]


def _fish_quote(location):
"""Quote a path so fish treats it as a single token.

Unquoted spaces in ``set PATH ...`` split the entry (fish-shell/fish-shell#527).
Double-quote and escape ``\\``, ``"``, and ``$`` so Hatch-style paths like
``.../Application Support/...`` survive into config.fish.
"""
escaped = location.replace('\\', '\\\\').replace('"', '\\"').replace('$', '\\$')
return '"{}"'.format(escaped)


class Fish(Shell):
def config(self, location, front=True):
location = ' '.join(location.split(pathsep))
head, tail = (location, '$PATH') if front else ('$PATH', location)
# PATH is a list in fish; quote each entry so spaces are not word-split.
quoted = ' '.join(_fish_quote(part) for part in location.split(pathsep))
head, tail = (quoted, '$PATH') if front else ('$PATH', quoted)

# https://github.com/fish-shell/fish-shell/issues/527#issuecomment-12436286
contents = 'set PATH {} {}'.format(head, tail)
Expand Down