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

This commit is contained in:
Serhiy Storchaka 2017-09-26 00:55:55 +03:00 committed by GitHub
parent f1502d097c
commit 81108375d9
8 changed files with 59 additions and 49 deletions

View File

@ -84,15 +84,12 @@ __all__ = [
import collections as _collections import collections as _collections
import copy as _copy
import os as _os import os as _os
import re as _re import re as _re
import sys as _sys import sys as _sys
import textwrap as _textwrap
from gettext import gettext as _, ngettext from gettext import gettext as _, ngettext
SUPPRESS = '==SUPPRESS==' SUPPRESS = '==SUPPRESS=='
OPTIONAL = '?' OPTIONAL = '?'
@ -137,10 +134,16 @@ class _AttributeHolder(object):
return [] return []
def _ensure_value(namespace, name, value): def _copy_items(items):
if getattr(namespace, name, None) is None: if items is None:
setattr(namespace, name, value) return []
return getattr(namespace, name) # 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,11 +622,16 @@ class HelpFormatter(object):
def _split_lines(self, text, width): def _split_lines(self, text, width):
text = self._whitespace_matcher.sub(' ', text).strip() 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): def _fill_text(self, text, width, indent):
text = self._whitespace_matcher.sub(' ', text).strip() text = self._whitespace_matcher.sub(' ', text).strip()
return _textwrap.fill(text, width, initial_indent=indent, import textwrap
return textwrap.fill(text, width,
initial_indent=indent,
subsequent_indent=indent) subsequent_indent=indent)
def _get_help_string(self, action): def _get_help_string(self, action):
@ -952,7 +960,8 @@ class _AppendAction(Action):
metavar=metavar) metavar=metavar)
def __call__(self, parser, namespace, values, option_string=None): 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) items.append(values)
setattr(namespace, self.dest, items) setattr(namespace, self.dest, items)
@ -978,7 +987,8 @@ class _AppendConstAction(Action):
metavar=metavar) metavar=metavar)
def __call__(self, parser, namespace, values, option_string=None): 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) items.append(self.const)
setattr(namespace, self.dest, items) setattr(namespace, self.dest, items)
@ -1000,8 +1010,10 @@ class _CountAction(Action):
help=help) help=help)
def __call__(self, parser, namespace, values, option_string=None): def __call__(self, parser, namespace, values, option_string=None):
new_count = _ensure_value(namespace, self.dest, 0) + 1 count = getattr(namespace, self.dest, None)
setattr(namespace, self.dest, new_count) if count is None:
count = 0
setattr(namespace, self.dest, count + 1)
class _HelpAction(Action): class _HelpAction(Action):

View File

@ -1,7 +1,5 @@
import sys import sys
from types import MappingProxyType, DynamicClassAttribute from types import MappingProxyType, DynamicClassAttribute
from functools import reduce
from operator import or_ as _or_
# try _collections first to reduce startup cost # try _collections first to reduce startup cost
try: try:
@ -744,11 +742,10 @@ class Flag(Enum):
def __invert__(self): def __invert__(self):
members, uncovered = _decompose(self.__class__, self._value_) members, uncovered = _decompose(self.__class__, self._value_)
inverted_members = [ inverted = self.__class__(0)
m for m in self.__class__ for m in self.__class__:
if m not in members and not m._value_ & self._value_ if m not in members and not (m._value_ & self._value_):
] inverted = inverted | m
inverted = reduce(_or_, inverted_members, self.__class__(0))
return self.__class__(inverted) return self.__class__(inverted)

View File

@ -46,13 +46,10 @@ internationalized, to the local language and cultural habits.
# find this format documented anywhere. # find this format documented anywhere.
import copy
import locale import locale
import os import os
import re import re
import struct
import sys import sys
from errno import ENOENT
__all__ = ['NullTranslations', 'GNUTranslations', 'Catalog', __all__ = ['NullTranslations', 'GNUTranslations', 'Catalog',
@ -342,7 +339,9 @@ class GNUTranslations(NullTranslations):
def _parse(self, fp): def _parse(self, fp):
"""Override this method to support alternative .mo formats.""" """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', '') filename = getattr(fp, 'name', '')
# Parse the .mo file header, which consists of 5 little endian 32 # Parse the .mo file header, which consists of 5 little endian 32
# bit words. # bit words.
@ -520,7 +519,9 @@ def translation(domain, localedir=None, languages=None,
if not mofiles: if not mofiles:
if fallback: if fallback:
return NullTranslations() 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 # Avoid opening, reading, and parsing the .mo file after it's been done
# once. # once.
result = None result = None
@ -533,6 +534,9 @@ def translation(domain, localedir=None, languages=None,
# Copy the translation object to allow setting fallbacks and # Copy the translation object to allow setting fallbacks and
# output charset. All other instance data is shared with the # output charset. All other instance data is shared with the
# cached object. # cached object.
# Delay copy import for speeding up gettext import when .mo files
# are not used.
import copy
t = copy.copy(t) t = copy.copy(t)
if codeset: if codeset:
t.set_output_charset(codeset) t.set_output_charset(codeset)

View File

@ -14,10 +14,9 @@ import sys
import encodings import encodings
import encodings.aliases import encodings.aliases
import re import re
import collections.abc import _collections_abc
from builtins import str as _builtin_str from builtins import str as _builtin_str
import functools import functools
import warnings
# Try importing the _locale module. # Try importing the _locale module.
# #
@ -215,7 +214,7 @@ def format_string(f, val, grouping=False, monetary=False):
percents = list(_percent_re.finditer(f)) percents = list(_percent_re.finditer(f))
new_f = _percent_re.sub('%s', f) new_f = _percent_re.sub('%s', f)
if isinstance(val, collections.abc.Mapping): if isinstance(val, _collections_abc.Mapping):
new_val = [] new_val = []
for perc in percents: for perc in percents:
if perc.group()[-1]=='%': 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): def format(percent, value, grouping=False, monetary=False, *additional):
"""Deprecated, use format_string instead.""" """Deprecated, use format_string instead."""
import warnings
warnings.warn( warnings.warn(
"This method will be removed in a future version of Python. " "This method will be removed in a future version of Python. "
"Use 'locale.format_string()' instead.", "Use 'locale.format_string()' instead.",

View File

@ -23,7 +23,7 @@ and opendir), and leave all pathname manipulation to os.path
#' #'
import abc import abc
import sys, errno import sys
import stat as st import stat as st
_names = sys.builtin_module_names _names = sys.builtin_module_names
@ -590,12 +590,10 @@ def _execvpe(file, args, env=None):
argrest = (args,) argrest = (args,)
env = environ env = environ
head, tail = path.split(file) if path.dirname(file):
if head:
exec_func(file, *argrest) exec_func(file, *argrest)
return return
last_exc = saved_exc = None saved_exc = None
saved_tb = None
path_list = get_exec_path(env) path_list = get_exec_path(env)
if name != 'nt': if name != 'nt':
file = fsencode(file) file = fsencode(file)
@ -604,16 +602,15 @@ def _execvpe(file, args, env=None):
fullname = path.join(dir, file) fullname = path.join(dir, file)
try: try:
exec_func(fullname, *argrest) exec_func(fullname, *argrest)
except (FileNotFoundError, NotADirectoryError) as e:
last_exc = e
except OSError as e: except OSError as e:
last_exc = e last_exc = e
tb = sys.exc_info()[2] if saved_exc is None:
if (e.errno != errno.ENOENT and e.errno != errno.ENOTDIR
and saved_exc is None):
saved_exc = e saved_exc = e
saved_tb = tb if saved_exc is not None:
if saved_exc: raise saved_exc
raise saved_exc.with_traceback(saved_tb) raise last_exc
raise last_exc.with_traceback(tb)
def get_exec_path(env=None): def get_exec_path(env=None):

View File

@ -6,7 +6,7 @@ import os
import posixpath import posixpath
import re import re
import sys import sys
from collections.abc import Sequence from _collections_abc import Sequence
from errno import EINVAL, ENOENT, ENOTDIR from errno import EINVAL, ENOENT, ENOTDIR
from operator import attrgetter from operator import attrgetter
from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO

View File

@ -172,9 +172,6 @@ class DynamicClassAttribute:
return result return result
import functools as _functools
import collections.abc as _collections_abc
class _GeneratorWrapper: class _GeneratorWrapper:
# TODO: Implement this in C. # TODO: Implement this in C.
def __init__(self, gen): def __init__(self, gen):
@ -247,7 +244,10 @@ def coroutine(func):
# return generator-like objects (for instance generators # return generator-like objects (for instance generators
# compiled with Cython). # 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): def wrapped(*args, **kwargs):
coro = func(*args, **kwargs) coro = func(*args, **kwargs)
if (coro.__class__ is CoroutineType or if (coro.__class__ is CoroutineType or

View File

@ -21,7 +21,7 @@ from _weakref import (
from _weakrefset import WeakSet, _IterationGuard 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 sys
import itertools import itertools
@ -87,7 +87,7 @@ class WeakMethod(ref):
__hash__ = ref.__hash__ __hash__ = ref.__hash__
class WeakValueDictionary(collections.abc.MutableMapping): class WeakValueDictionary(_collections_abc.MutableMapping):
"""Mapping class that references values weakly. """Mapping class that references values weakly.
Entries in the dictionary will be discarded when no strong Entries in the dictionary will be discarded when no strong
@ -340,7 +340,7 @@ class KeyedRef(ref):
super().__init__(ob, callback) super().__init__(ob, callback)
class WeakKeyDictionary(collections.abc.MutableMapping): class WeakKeyDictionary(_collections_abc.MutableMapping):
""" Mapping class that references keys weakly. """ Mapping class that references keys weakly.
Entries in the dictionary will be discarded when there is no Entries in the dictionary will be discarded when there is no