Skip to content

Commit 06a26fd

Browse files
authored
gh-118761: Optimise import time for shlex (#132036)
1 parent 984a314 commit 06a26fd

File tree

3 files changed

+17
-7
lines changed

3 files changed

+17
-7
lines changed

Lib/shlex.py

+10-7
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,17 @@ 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+
# Use bytes.translate() for performance
326+
safe_chars = (b'%+,-./0123456789:=@'
327+
b'ABCDEFGHIJKLMNOPQRSTUVWXYZ_'
328+
b'abcdefghijklmnopqrstuvwxyz')
329+
# No quoting is needed if `s` is an ASCII string consisting only of `safe_chars`
330+
if s.isascii() and not s.encode().translate(None, delete=safe_chars):
328331
return s
329332

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

Lib/test/test_shlex.py

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import shlex
44
import string
55
import unittest
6+
from test.support import import_helper
67

78

89
# The original test data set was from shellwords, by Hartmut Goebel.
@@ -363,6 +364,9 @@ def testPunctuationCharsReadOnly(self):
363364
with self.assertRaises(AttributeError):
364365
shlex_instance.punctuation_chars = False
365366

367+
def test_lazy_imports(self):
368+
import_helper.ensure_lazy_imports('shlex', {'collections', 're', 'os'})
369+
366370

367371
# Allow this test to be used with old shlex.py
368372
if not getattr(shlex, "split", None):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Improve import times by up to 33x for the :mod:`shlex` module,
2+
and improve the performance of :func:`shlex.quote` by up to 12x.
3+
Patch by Adam Turner.

0 commit comments

Comments
 (0)