Skip to content

Commit d767b5f

Browse files
CPython Developersyouknowone
authored andcommitted
Update shlex from v3.14.2-288-g06f9c8ca1c
1 parent 89dbd42 commit d767b5f

2 files changed

Lines changed: 26 additions & 15 deletions

File tree

Lib/shlex.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,7 @@
77
# iterator interface by Gustavo Niemeyer, April 2003.
88
# changes to tokenize more like Posix shells by Vinay Sajip, July 2016.
99

10-
import os
11-
import re
1210
import sys
13-
from collections import deque
14-
1511
from io import StringIO
1612

1713
__all__ = ["shlex", "split", "quote", "join"]
@@ -20,6 +16,8 @@ class shlex:
2016
"A lexical analyzer class for simple shell-like syntaxes."
2117
def __init__(self, instream=None, infile=None, posix=False,
2218
punctuation_chars=False):
19+
from collections import deque # deferred import for performance
20+
2321
if isinstance(instream, str):
2422
instream = StringIO(instream)
2523
if instream is not None:
@@ -278,6 +276,7 @@ def read_token(self):
278276

279277
def sourcehook(self, newfile):
280278
"Hook called on a filename to be sourced."
279+
import os.path
281280
if newfile[0] == '"':
282281
newfile = newfile[1:-1]
283282
# This implements cpp-like semantics for relative-path inclusion.
@@ -318,13 +317,20 @@ def join(split_command):
318317
return ' '.join(quote(arg) for arg in split_command)
319318

320319

321-
_find_unsafe = re.compile(r'[^\w@%+=:,./-]', re.ASCII).search
322-
323320
def quote(s):
324321
"""Return a shell-escaped version of the string *s*."""
325322
if not s:
326323
return "''"
327-
if _find_unsafe(s) is None:
324+
325+
if not isinstance(s, str):
326+
raise TypeError(f"expected string object, got {type(s).__name__!r}")
327+
328+
# Use bytes.translate() for performance
329+
safe_chars = (b'%+,-./0123456789:=@'
330+
b'ABCDEFGHIJKLMNOPQRSTUVWXYZ_'
331+
b'abcdefghijklmnopqrstuvwxyz')
332+
# No quoting is needed if `s` is an ASCII string consisting only of `safe_chars`
333+
if s.isascii() and not s.encode().translate(None, delete=safe_chars):
328334
return s
329335

330336
# use single quotes, and put single quotes into double quotes

Lib/test/test_shlex.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import shlex
44
import string
55
import unittest
6+
from test.support import cpython_only
7+
from test.support import import_helper
68

79

810
# The original test data set was from shellwords, by Hartmut Goebel.
@@ -165,14 +167,12 @@ def testSplitNone(self):
165167
with self.assertRaises(ValueError):
166168
shlex.split(None)
167169

168-
# TODO: RUSTPYTHON; ValueError: Error Retrieving Value
169-
@unittest.expectedFailure
170+
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError: Error Retrieving Value
170171
def testSplitPosix(self):
171172
"""Test data splitting with posix parser"""
172173
self.splitTest(self.posix_data, comments=True)
173174

174-
# TODO: RUSTPYTHON; ValueError: Error Retrieving Value
175-
@unittest.expectedFailure
175+
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError: Error Retrieving Value
176176
def testCompat(self):
177177
"""Test compatibility interface"""
178178
for i in range(len(self.data)):
@@ -313,8 +313,7 @@ def testEmptyStringHandling(self):
313313
s = shlex.shlex("'')abc", punctuation_chars=True)
314314
self.assertEqual(list(s), expected)
315315

316-
# TODO: RUSTPYTHON; ValueError: Error Retrieving Value
317-
@unittest.expectedFailure
316+
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError: Error Retrieving Value
318317
def testUnicodeHandling(self):
319318
"""Test punctuation_chars and whitespace_split handle unicode."""
320319
ss = "\u2119\u01b4\u2602\u210c\u00f8\u1f24"
@@ -334,6 +333,7 @@ def testQuote(self):
334333
unsafe = '"`$\\!' + unicode_sample
335334

336335
self.assertEqual(shlex.quote(''), "''")
336+
self.assertEqual(shlex.quote(None), "''")
337337
self.assertEqual(shlex.quote(safeunquoted), safeunquoted)
338338
self.assertEqual(shlex.quote('test file name'), "'test file name'")
339339
for u in unsafe:
@@ -342,6 +342,8 @@ def testQuote(self):
342342
for u in unsafe:
343343
self.assertEqual(shlex.quote("test%s'name'" % u),
344344
"'test%s'\"'\"'name'\"'\"''" % u)
345+
self.assertRaises(TypeError, shlex.quote, 42)
346+
self.assertRaises(TypeError, shlex.quote, b"abc")
345347

346348
def testJoin(self):
347349
for split_command, command in [
@@ -354,8 +356,7 @@ def testJoin(self):
354356
joined = shlex.join(split_command)
355357
self.assertEqual(joined, command)
356358

357-
# TODO: RUSTPYTHON; ValueError: Error Retrieving Value
358-
@unittest.expectedFailure
359+
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError: Error Retrieving Value
359360
def testJoinRoundtrip(self):
360361
all_data = self.data + self.posix_data
361362
for command, *split_command in all_data:
@@ -371,6 +372,10 @@ def testPunctuationCharsReadOnly(self):
371372
with self.assertRaises(AttributeError):
372373
shlex_instance.punctuation_chars = False
373374

375+
@cpython_only
376+
def test_lazy_imports(self):
377+
import_helper.ensure_lazy_imports('shlex', {'collections', 're', 'os'})
378+
374379

375380
# Allow this test to be used with old shlex.py
376381
if not getattr(shlex, "split", None):

0 commit comments

Comments
 (0)