Skip to content

Commit 9b68237

Browse files
committed
Make changes on normalization of path and some cleanups
1 parent e0f4c2b commit 9b68237

File tree

7 files changed

+100
-61
lines changed

7 files changed

+100
-61
lines changed

src/pip/_internal/__init__.py

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ def autocomplete():
116116
options = [(k, v) for k, v in options if k.startswith(current)]
117117
# get completion type given cwords and available subcommand options
118118
completion_type = get_path_completion_type(
119-
cwords, cword, subcommand.parser.option_list_all)
119+
cwords, cword, subcommand.parser.option_list_all,
120+
)
120121
# get completion files and directories if ``completion_type`` is
121122
# ``<file>``, ``<dir>`` or ``<path>``
122123
if completion_type:
@@ -147,22 +148,23 @@ def autocomplete():
147148

148149

149150
def get_path_completion_type(cwords, cword, opts):
150-
"""Get the type of path completion(``file``, ``dir``, ``path`` or None)
151+
"""Get the type of path completion (``file``, ``dir``, ``path`` or None)
151152
152153
:param cwords: same as the environmental variable ``COMP_WORDS``
153154
:param cword: same as the environmental variable ``COMP_CWORD``
154155
:param opts: The available options to check
155-
:return: path completion type(``file``, ``dir``, ``path`` or None)
156+
:return: path completion type (``file``, ``dir``, ``path`` or None)
156157
"""
157-
if cword >= 2 and cwords[cword - 2].startswith('-'):
158-
for opt in opts:
159-
if opt.help == optparse.SUPPRESS_HELP:
160-
continue
161-
for o in str(opt).split('/'):
162-
if cwords[cword - 2].split('=')[0] == o:
163-
if any(x in ('path', 'file', 'dir')
164-
for x in opt.metavar.split('/')):
165-
return opt.metavar
158+
if cword < 2 or not cwords[cword - 2].startswith('-'):
159+
return
160+
for opt in opts:
161+
if opt.help == optparse.SUPPRESS_HELP:
162+
continue
163+
for o in str(opt).split('/'):
164+
if cwords[cword - 2].split('=')[0] == o:
165+
if any(x in ('path', 'file', 'dir')
166+
for x in opt.metavar.split('/')):
167+
return opt.metavar
166168

167169

168170
def auto_complete_paths(current, completion_type):
@@ -174,25 +176,25 @@ def auto_complete_paths(current, completion_type):
174176
:param completion_type: path completion type(`file`, `path` or `dir`)i
175177
:return: A generator of regular files and/or directories
176178
"""
177-
# split ``current`` into two parts(directory and name)
178179
directory, filename = os.path.split(current)
179-
# change directory to ``directory``
180180
current_path = os.path.abspath(directory)
181-
# check whether ``current_path`` is accessible
182-
if os.access(current_path, os.R_OK):
183-
# list all files that start with ``filename``
184-
file_list = (x for x in os.listdir(current_path)
185-
if x.startswith(filename))
186-
for f in file_list:
187-
opt = os.path.join(current_path, f)
188-
comp_file = os.path.join(directory, f)
189-
# complete regular files when there is not ``<dir>`` after option
190-
# complete directories when there is ``<file>``, ``<path>`` or
191-
# ``<dir>``after option
192-
if completion_type != 'dir' and os.path.isfile(opt):
193-
yield comp_file
194-
elif os.path.isdir(opt):
195-
yield os.path.join(comp_file, '')
181+
# Don't complete paths if they can't be accessed
182+
if not os.access(current_path, os.R_OK):
183+
return
184+
filename = os.path.normcase(filename)
185+
# list all files that start with ``filename``
186+
file_list = (x for x in os.listdir(current_path)
187+
if os.path.normcase(x).startswith(filename))
188+
for f in file_list:
189+
opt = os.path.join(current_path, f)
190+
comp_file = os.path.normcase(os.path.join(directory, f))
191+
# complete regular files when there is not ``<dir>`` after option
192+
# complete directories when there is ``<file>``, ``<path>`` or
193+
# ``<dir>``after option
194+
if completion_type != 'dir' and os.path.isfile(opt):
195+
yield comp_file
196+
elif os.path.isdir(opt):
197+
yield os.path.join(comp_file, '')
196198

197199

198200
def create_main_parser():

tests/data/completion_paths/README.txt

Whitespace-only changes.

tests/data/completion_paths/REPLAY/video.mpeg

Whitespace-only changes.

tests/data/completion_paths/requirements.txt

Whitespace-only changes.

tests/data/completion_paths/resources/images/icon.png

Loading

tests/functional/test_completion.py

Lines changed: 67 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -128,19 +128,34 @@ def test_completion_files_after_option(script, data):
128128
(e.g. ``pip install -r``)
129129
"""
130130
res, env = setup_completion(
131-
script, ('pip install -r r'),
132-
'3',
133-
data.complete_paths
131+
script=script,
132+
words=('pip install -r r'),
133+
cword='3',
134+
cwd=data.completion_paths,
134135
)
135-
assert 'requirements.txt' in res.stdout,\
136-
"autocomplete function could not complete <file>"\
136+
assert 'requirements.txt' in res.stdout, (
137+
"autocomplete function could not complete <file> "
137138
"after options in command"
138-
assert 'README.txt' not in res.stdout,\
139-
"autocomplete function completed <file> that"\
139+
)
140+
assert os.path.join('resources', '') in res.stdout, (
141+
"autocomplete function could not complete <dir> "
142+
"after options in command"
143+
)
144+
assert not any(out in res.stdout for out in
145+
(os.path.join('REPLAY', ''), 'README.txt')), (
146+
"autocomplete function completed <file> or <dir> that "
140147
"should not be completed"
141-
assert 'resources' in res.stdout,\
142-
"autocomplete function could not complete <dir>"\
148+
)
149+
if sys.platform != 'win32':
150+
return
151+
assert 'readme.txt' in res.stdout, (
152+
"autocomplete function could not complete <file> "
143153
"after options in command"
154+
)
155+
assert os.path.join('replay', '') in res.stdout, (
156+
"autocomplete function could not complete <dir> "
157+
"after options in command"
158+
)
144159

145160

146161
def test_completion_not_files_after_option(script, data):
@@ -149,13 +164,21 @@ def test_completion_not_files_after_option(script, data):
149164
(e.g. ``pip install``)
150165
"""
151166
res, env = setup_completion(
152-
script, ('pip install r'),
153-
'2',
154-
data.complete_paths
167+
script=script,
168+
words=('pip install r'),
169+
cword='2',
170+
cwd=data.completion_paths,
155171
)
156-
assert 'requirements.txt' not in res.stdout,\
157-
"autocomplete function completed <file> when"\
172+
assert not any(out in res.stdout for out in
173+
('requirements.txt', 'readme.txt',)), (
174+
"autocomplete function completed <file> when "
158175
"it should not complete"
176+
)
177+
assert not any(os.path.join(out, '') in res.stdout
178+
for out in ('replay', 'resources')), (
179+
"autocomplete function completed <dir> when "
180+
"it should not complete"
181+
)
159182

160183

161184
def test_completion_directories_after_option(script, data):
@@ -164,13 +187,23 @@ def test_completion_directories_after_option(script, data):
164187
(e.g. ``pip --cache-dir``)
165188
"""
166189
res, env = setup_completion(
167-
script,
168-
('pip --cache-dir resources'),
169-
'2',
170-
data.complete_paths
190+
script=script,
191+
words=('pip --cache-dir r'),
192+
cword='2',
193+
cwd=data.completion_paths,
171194
)
172-
assert os.path.join('resources', '') in res.stdout,\
195+
assert os.path.join('resources', '') in res.stdout, (
173196
"autocomplete function could not complete <dir> after options"
197+
)
198+
assert not any(out in res.stdout for out in (
199+
'requirements.txt', 'README.txt', os.path.join('REPLAY', ''))), (
200+
"autocomplete function completed <dir> when "
201+
"it should not complete"
202+
)
203+
if sys.platform == 'win32':
204+
assert os.path.join('replay', '') in res.stdout, (
205+
"autocomplete function could not complete <dir> after options"
206+
)
174207

175208

176209
def test_completion_subdirectories_after_option(script, data):
@@ -179,15 +212,16 @@ def test_completion_subdirectories_after_option(script, data):
179212
given path of a directory
180213
"""
181214
res, env = setup_completion(
182-
script,
183-
('pip --cache-dir ' + os.path.join('resources', '')),
184-
'2',
185-
data.complete_paths
215+
script=script,
216+
words=('pip --cache-dir ' + os.path.join('resources', '')),
217+
cword='2',
218+
cwd=data.completion_paths,
186219
)
187220
assert os.path.join('resources',
188-
os.path.join('images', '')) in res.stdout,\
189-
"autocomplete function could not complete <dir>"\
221+
os.path.join('images', '')) in res.stdout, (
222+
"autocomplete function could not complete <dir> "
190223
"given path of a directory after options"
224+
)
191225

192226

193227
def test_completion_path_after_option(script, data):
@@ -196,13 +230,16 @@ def test_completion_path_after_option(script, data):
196230
given absolute path
197231
"""
198232
res, env = setup_completion(
199-
script,
200-
('pip install -e ' + os.path.join(data.complete_paths, 'R')),
201-
'3'
233+
script=script,
234+
words=('pip install -e ' + os.path.join(data.completion_paths, 'R')),
235+
cword='3',
202236
)
203-
assert os.path.join(data.complete_paths, 'README.txt') in res.stdout,\
204-
"autocomplete function could not complete <path>"\
237+
assert all(os.path.normcase(os.path.join(data.completion_paths, out))
238+
in res.stdout for out in (
239+
'README.txt', os.path.join('REPLAY', ''))), (
240+
"autocomplete function could not complete <path> "
205241
"after options in command given absolute path"
242+
)
206243

207244

208245
@pytest.mark.parametrize('flag', ['--bash', '--zsh', '--fish'])

tests/lib/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ def reqfiles(self):
113113
return self.root.join("reqfiles")
114114

115115
@property
116-
def complete_paths(self):
117-
return self.root.join("completepaths")
116+
def completion_paths(self):
117+
return self.root.join("completion_paths")
118118

119119
@property
120120
def find_links(self):

0 commit comments

Comments
 (0)