[Python-checkins] bpo-30152: Reduce the number of imports for argparse. (#1269)

Serhiy Storchaka webhook-mailer at python.org
Mon Sep 25 17:56:02 EDT 2017


https://github.com/python/cpython/commit/81108375d9b2ccd0add1572da745311d4dfac505
commit: 81108375d9b2ccd0add1572da745311d4dfac505
branch: master
author: Serhiy Storchaka <storchaka at gmail.com>
committer: GitHub <noreply at github.com>
date: 2017-09-26T00:55:55+03:00
summary:

bpo-30152: Reduce the number of imports for argparse. (#1269)

files:
M Lib/argparse.py
M Lib/enum.py
M Lib/gettext.py
M Lib/locale.py
M Lib/os.py
M Lib/pathlib.py
M Lib/types.py
M Lib/weakref.py

diff --git a/Lib/argparse.py b/Lib/argparse.py
index 98bbed0e508..d8bbd352fd3 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -84,15 +84,12 @@
 
 
 import collections as _collections
-import copy as _copy
 import os as _os
 import re as _re
 import sys as _sys
-import textwrap as _textwrap
 
 from gettext import gettext as _, ngettext
 
-
 SUPPRESS = '==SUPPRESS=='
 
 OPTIONAL = '?'
@@ -137,10 +134,16 @@ def _get_args(self):
         return []
 
 
-def _ensure_value(namespace, name, value):
-    if getattr(namespace, name, None) is None:
-        setattr(namespace, name, value)
-    return getattr(namespace, name)
+def _copy_items(items):
+    if items is None:
+        return []
+    # The copy module is used only in the 'append' and 'append_const'
+    # actions, and it is needed only when the default value isn't a list.
+    # Delay its import for speeding up the common case.
+    if type(items) is list:
+        return items[:]
+    import copy
+    return copy.copy(items)
 
 
 # ===============
@@ -619,12 +622,17 @@ def _iter_indented_subactions(self, action):
 
     def _split_lines(self, text, width):
         text = self._whitespace_matcher.sub(' ', text).strip()
-        return _textwrap.wrap(text, width)
+        # The textwrap module is used only for formatting help.
+        # Delay its import for speeding up the common usage of argparse.
+        import textwrap
+        return textwrap.wrap(text, width)
 
     def _fill_text(self, text, width, indent):
         text = self._whitespace_matcher.sub(' ', text).strip()
-        return _textwrap.fill(text, width, initial_indent=indent,
-                                           subsequent_indent=indent)
+        import textwrap
+        return textwrap.fill(text, width,
+                             initial_indent=indent,
+                             subsequent_indent=indent)
 
     def _get_help_string(self, action):
         return action.help
@@ -952,7 +960,8 @@ def __init__(self,
             metavar=metavar)
 
     def __call__(self, parser, namespace, values, option_string=None):
-        items = _copy.copy(_ensure_value(namespace, self.dest, []))
+        items = getattr(namespace, self.dest, None)
+        items = _copy_items(items)
         items.append(values)
         setattr(namespace, self.dest, items)
 
@@ -978,7 +987,8 @@ def __init__(self,
             metavar=metavar)
 
     def __call__(self, parser, namespace, values, option_string=None):
-        items = _copy.copy(_ensure_value(namespace, self.dest, []))
+        items = getattr(namespace, self.dest, None)
+        items = _copy_items(items)
         items.append(self.const)
         setattr(namespace, self.dest, items)
 
@@ -1000,8 +1010,10 @@ def __init__(self,
             help=help)
 
     def __call__(self, parser, namespace, values, option_string=None):
-        new_count = _ensure_value(namespace, self.dest, 0) + 1
-        setattr(namespace, self.dest, new_count)
+        count = getattr(namespace, self.dest, None)
+        if count is None:
+            count = 0
+        setattr(namespace, self.dest, count + 1)
 
 
 class _HelpAction(Action):
diff --git a/Lib/enum.py b/Lib/enum.py
index 6d90f7dd65f..fe7cb20fc06 100644
--- a/Lib/enum.py
+++ b/Lib/enum.py
@@ -1,7 +1,5 @@
 import sys
 from types import MappingProxyType, DynamicClassAttribute
-from functools import reduce
-from operator import or_ as _or_
 
 # try _collections first to reduce startup cost
 try:
@@ -744,11 +742,10 @@ def __xor__(self, other):
 
     def __invert__(self):
         members, uncovered = _decompose(self.__class__, self._value_)
-        inverted_members = [
-                m for m in self.__class__
-                if m not in members and not m._value_ & self._value_
-                ]
-        inverted = reduce(_or_, inverted_members, self.__class__(0))
+        inverted = self.__class__(0)
+        for m in self.__class__:
+            if m not in members and not (m._value_ & self._value_):
+                inverted = inverted | m
         return self.__class__(inverted)
 
 
diff --git a/Lib/gettext.py b/Lib/gettext.py
index 5ad7ff6bfce..4c3b80b0239 100644
--- a/Lib/gettext.py
+++ b/Lib/gettext.py
@@ -46,13 +46,10 @@
 #   find this format documented anywhere.
 
 
-import copy
 import locale
 import os
 import re
-import struct
 import sys
-from errno import ENOENT
 
 
 __all__ = ['NullTranslations', 'GNUTranslations', 'Catalog',
@@ -342,7 +339,9 @@ def _get_versions(self, version):
 
     def _parse(self, fp):
         """Override this method to support alternative .mo formats."""
-        unpack = struct.unpack
+        # Delay struct import for speeding up gettext import when .mo files
+        # are not used.
+        from struct import unpack
         filename = getattr(fp, 'name', '')
         # Parse the .mo file header, which consists of 5 little endian 32
         # bit words.
@@ -520,7 +519,9 @@ def translation(domain, localedir=None, languages=None,
     if not mofiles:
         if fallback:
             return NullTranslations()
-        raise OSError(ENOENT, 'No translation file found for domain', domain)
+        from errno import ENOENT
+        raise FileNotFoundError(ENOENT,
+                                'No translation file found for domain', domain)
     # Avoid opening, reading, and parsing the .mo file after it's been done
     # once.
     result = None
@@ -533,6 +534,9 @@ def translation(domain, localedir=None, languages=None,
         # Copy the translation object to allow setting fallbacks and
         # output charset. All other instance data is shared with the
         # cached object.
+        # Delay copy import for speeding up gettext import when .mo files
+        # are not used.
+        import copy
         t = copy.copy(t)
         if codeset:
             t.set_output_charset(codeset)
diff --git a/Lib/locale.py b/Lib/locale.py
index 569fe854dbb..f1d157d6f9c 100644
--- a/Lib/locale.py
+++ b/Lib/locale.py
@@ -14,10 +14,9 @@
 import encodings
 import encodings.aliases
 import re
-import collections.abc
+import _collections_abc
 from builtins import str as _builtin_str
 import functools
-import warnings
 
 # Try importing the _locale module.
 #
@@ -215,7 +214,7 @@ def format_string(f, val, grouping=False, monetary=False):
     percents = list(_percent_re.finditer(f))
     new_f = _percent_re.sub('%s', f)
 
-    if isinstance(val, collections.abc.Mapping):
+    if isinstance(val, _collections_abc.Mapping):
         new_val = []
         for perc in percents:
             if perc.group()[-1]=='%':
@@ -244,6 +243,7 @@ def format_string(f, val, grouping=False, monetary=False):
 
 def format(percent, value, grouping=False, monetary=False, *additional):
     """Deprecated, use format_string instead."""
+    import warnings
     warnings.warn(
         "This method will be removed in a future version of Python. "
         "Use 'locale.format_string()' instead.",
diff --git a/Lib/os.py b/Lib/os.py
index 807ddb56c06..4f9fdf5b0ea 100644
--- a/Lib/os.py
+++ b/Lib/os.py
@@ -23,7 +23,7 @@
 
 #'
 import abc
-import sys, errno
+import sys
 import stat as st
 
 _names = sys.builtin_module_names
@@ -590,12 +590,10 @@ def _execvpe(file, args, env=None):
         argrest = (args,)
         env = environ
 
-    head, tail = path.split(file)
-    if head:
+    if path.dirname(file):
         exec_func(file, *argrest)
         return
-    last_exc = saved_exc = None
-    saved_tb = None
+    saved_exc = None
     path_list = get_exec_path(env)
     if name != 'nt':
         file = fsencode(file)
@@ -604,16 +602,15 @@ def _execvpe(file, args, env=None):
         fullname = path.join(dir, file)
         try:
             exec_func(fullname, *argrest)
+        except (FileNotFoundError, NotADirectoryError) as e:
+            last_exc = e
         except OSError as e:
             last_exc = e
-            tb = sys.exc_info()[2]
-            if (e.errno != errno.ENOENT and e.errno != errno.ENOTDIR
-                and saved_exc is None):
+            if saved_exc is None:
                 saved_exc = e
-                saved_tb = tb
-    if saved_exc:
-        raise saved_exc.with_traceback(saved_tb)
-    raise last_exc.with_traceback(tb)
+    if saved_exc is not None:
+        raise saved_exc
+    raise last_exc
 
 
 def get_exec_path(env=None):
diff --git a/Lib/pathlib.py b/Lib/pathlib.py
index c14ddd03356..73dd513b200 100644
--- a/Lib/pathlib.py
+++ b/Lib/pathlib.py
@@ -6,7 +6,7 @@
 import posixpath
 import re
 import sys
-from collections.abc import Sequence
+from _collections_abc import Sequence
 from errno import EINVAL, ENOENT, ENOTDIR
 from operator import attrgetter
 from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
diff --git a/Lib/types.py b/Lib/types.py
index 929cba223aa..336918fea09 100644
--- a/Lib/types.py
+++ b/Lib/types.py
@@ -172,9 +172,6 @@ def deleter(self, fdel):
         return result
 
 
-import functools as _functools
-import collections.abc as _collections_abc
-
 class _GeneratorWrapper:
     # TODO: Implement this in C.
     def __init__(self, gen):
@@ -247,7 +244,10 @@ def coroutine(func):
     # return generator-like objects (for instance generators
     # compiled with Cython).
 
-    @_functools.wraps(func)
+    # Delay functools and _collections_abc import for speeding up types import.
+    import functools
+    import _collections_abc
+    @functools.wraps(func)
     def wrapped(*args, **kwargs):
         coro = func(*args, **kwargs)
         if (coro.__class__ is CoroutineType or
diff --git a/Lib/weakref.py b/Lib/weakref.py
index 1802f32a206..99de2eab741 100644
--- a/Lib/weakref.py
+++ b/Lib/weakref.py
@@ -21,7 +21,7 @@
 
 from _weakrefset import WeakSet, _IterationGuard
 
-import collections.abc  # Import after _weakref to avoid circular import.
+import _collections_abc  # Import after _weakref to avoid circular import.
 import sys
 import itertools
 
@@ -87,7 +87,7 @@ def __ne__(self, other):
     __hash__ = ref.__hash__
 
 
-class WeakValueDictionary(collections.abc.MutableMapping):
+class WeakValueDictionary(_collections_abc.MutableMapping):
     """Mapping class that references values weakly.
 
     Entries in the dictionary will be discarded when no strong
@@ -340,7 +340,7 @@ def __init__(self, ob, callback, key):
         super().__init__(ob, callback)
 
 
-class WeakKeyDictionary(collections.abc.MutableMapping):
+class WeakKeyDictionary(_collections_abc.MutableMapping):
     """ Mapping class that references keys weakly.
 
     Entries in the dictionary will be discarded when there is no



More information about the Python-checkins mailing list