Fixed #24979 -- Removed usage of inspect.getargspec().

This commit is contained in:
Tim Graham 2015-06-10 17:24:04 -04:00
parent 4b600ed244
commit 3872a33132
13 changed files with 118 additions and 40 deletions

View File

@ -14,6 +14,7 @@ from django.db import models
from django.http import Http404 from django.http import Http404
from django.template.engine import Engine from django.template.engine import Engine
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.inspect import func_has_no_args
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic import TemplateView from django.views.generic import TemplateView
@ -243,7 +244,7 @@ class ModelDetailView(BaseAdminDocsView):
# Gather model methods. # Gather model methods.
for func_name, func in model.__dict__.items(): for func_name, func in model.__dict__.items():
if (inspect.isfunction(func) and len(inspect.getargspec(func)[0]) == 1): if inspect.isfunction(func) and func_has_no_args(func):
try: try:
for exclude in MODEL_METHODS_EXCLUDE: for exclude in MODEL_METHODS_EXCLUDE:
if func_name.startswith(exclude): if func_name.startswith(exclude):

View File

@ -1,8 +1,8 @@
import argparse import argparse
import inspect
from django.contrib.gis import gdal from django.contrib.gis import gdal
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from django.utils.inspect import get_func_args
class LayerOptionAction(argparse.Action): class LayerOptionAction(argparse.Action):
@ -91,7 +91,7 @@ class Command(BaseCommand):
from django.contrib.gis.utils.ogrinspect import _ogrinspect, mapping from django.contrib.gis.utils.ogrinspect import _ogrinspect, mapping
# Filter options to params accepted by `_ogrinspect` # Filter options to params accepted by `_ogrinspect`
ogr_options = {k: v for k, v in options.items() ogr_options = {k: v for k, v in options.items()
if k in inspect.getargspec(_ogrinspect).args and v is not None} if k in get_func_args(_ogrinspect) and v is not None}
output = [s for s in _ogrinspect(ds, model_name, **ogr_options)] output = [s for s in _ogrinspect(ds, model_name, **ogr_options)]
if options['mapping']: if options['mapping']:

View File

@ -2,7 +2,6 @@ import errno
import os import os
import warnings import warnings
from datetime import datetime from datetime import datetime
from inspect import getargspec
from django.conf import settings from django.conf import settings
from django.core.exceptions import SuspiciousFileOperation from django.core.exceptions import SuspiciousFileOperation
@ -14,6 +13,7 @@ from django.utils.deconstruct import deconstructible
from django.utils.deprecation import RemovedInDjango20Warning from django.utils.deprecation import RemovedInDjango20Warning
from django.utils.encoding import filepath_to_uri, force_text from django.utils.encoding import filepath_to_uri, force_text
from django.utils.functional import LazyObject from django.utils.functional import LazyObject
from django.utils.inspect import func_supports_parameter
from django.utils.module_loading import import_string from django.utils.module_loading import import_string
from django.utils.six.moves.urllib.parse import urljoin from django.utils.six.moves.urllib.parse import urljoin
from django.utils.text import get_valid_filename from django.utils.text import get_valid_filename
@ -49,8 +49,7 @@ class Storage(object):
if not hasattr(content, 'chunks'): if not hasattr(content, 'chunks'):
content = File(content) content = File(content)
args, varargs, varkw, defaults = getargspec(self.get_available_name) if func_supports_parameter(self.get_available_name, 'max_length'):
if 'max_length' in args:
name = self.get_available_name(name, max_length=max_length) name = self.get_available_name(name, max_length=max_length)
else: else:
warnings.warn( warnings.warn(

View File

@ -3,7 +3,6 @@ from __future__ import unicode_literals
import collections import collections
import datetime import datetime
import decimal import decimal
import inspect
import math import math
import os import os
import re import re
@ -18,6 +17,7 @@ from django.utils import datetime_safe, six
from django.utils._os import upath from django.utils._os import upath
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.functional import Promise from django.utils.functional import Promise
from django.utils.inspect import get_func_args
from django.utils.module_loading import module_dir from django.utils.module_loading import module_dir
from django.utils.timezone import utc from django.utils.timezone import utc
from django.utils.version import get_docs_version from django.utils.version import get_docs_version
@ -97,7 +97,7 @@ class OperationWriter(object):
imports = set() imports = set()
name, args, kwargs = self.operation.deconstruct() name, args, kwargs = self.operation.deconstruct()
argspec = inspect.getargspec(self.operation.__init__) operation_args = get_func_args(self.operation.__init__)
# See if this operation is in django.db.migrations. If it is, # See if this operation is in django.db.migrations. If it is,
# We can just use the fact we already have that imported, # We can just use the fact we already have that imported,
@ -110,15 +110,14 @@ class OperationWriter(object):
self.indent() self.indent()
# Start at one because argspec includes "self" for i, arg in enumerate(args):
for i, arg in enumerate(args, 1):
arg_value = arg arg_value = arg
arg_name = argspec.args[i] arg_name = operation_args[i]
_write(arg_name, arg_value) _write(arg_name, arg_value)
i = len(args) i = len(args)
# Only iterate over remaining arguments # Only iterate over remaining arguments
for arg_name in argspec.args[i + 1:]: for arg_name in operation_args[i:]:
if arg_name in kwargs: # Don't sort to maintain signature order if arg_name in kwargs: # Don't sort to maintain signature order
arg_value = kwargs[arg_name] arg_value = kwargs[arg_name]
_write(arg_name, arg_value) _write(arg_name, arg_value)

View File

@ -1,7 +1,6 @@
import datetime import datetime
import os import os
import warnings import warnings
from inspect import getargspec
from django import forms from django import forms
from django.core import checks from django.core import checks
@ -13,6 +12,7 @@ from django.db.models.fields import Field
from django.utils import six from django.utils import six
from django.utils.deprecation import RemovedInDjango20Warning from django.utils.deprecation import RemovedInDjango20Warning
from django.utils.encoding import force_str, force_text from django.utils.encoding import force_str, force_text
from django.utils.inspect import func_supports_parameter
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -89,8 +89,7 @@ class FieldFile(File):
def save(self, name, content, save=True): def save(self, name, content, save=True):
name = self.field.generate_filename(self.instance, name) name = self.field.generate_filename(self.instance, name)
args, varargs, varkw, defaults = getargspec(self.storage.save) if func_supports_parameter(self.storage.save, 'max_length'):
if 'max_length' in args:
self.name = self.storage.save(name, content, max_length=self.field.max_length) self.name = self.storage.save(name, content, max_length=self.field.max_length)
else: else:
warnings.warn( warnings.warn(

View File

@ -290,8 +290,16 @@ class ConnectionRouter(object):
# If the router doesn't have a method, skip to the next one. # If the router doesn't have a method, skip to the next one.
continue continue
argspec = inspect.getargspec(router.allow_migrate) if six.PY3:
if len(argspec.args) == 3 and not argspec.keywords: sig = inspect.signature(router.allow_migrate)
has_deprecated_signature = not any(
p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()
)
else:
argspec = inspect.getargspec(router.allow_migrate)
has_deprecated_signature = len(argspec.args) == 3 and not argspec.keywords
if has_deprecated_signature:
warnings.warn( warnings.warn(
"The signature of allow_migrate has changed from " "The signature of allow_migrate has changed from "
"allow_migrate(self, db, model) to " "allow_migrate(self, db, model) to "

View File

@ -4,6 +4,7 @@ import warnings
import weakref import weakref
from django.utils.deprecation import RemovedInDjango21Warning from django.utils.deprecation import RemovedInDjango21Warning
from django.utils.inspect import func_accepts_kwargs
from django.utils.six.moves import range from django.utils.six.moves import range
if sys.version_info < (3, 4): if sys.version_info < (3, 4):
@ -89,24 +90,11 @@ class Signal(object):
# If DEBUG is on, check that we got a good receiver # If DEBUG is on, check that we got a good receiver
if settings.configured and settings.DEBUG: if settings.configured and settings.DEBUG:
import inspect
assert callable(receiver), "Signal receivers must be callable." assert callable(receiver), "Signal receivers must be callable."
# Check for **kwargs # Check for **kwargs
# Not all callables are inspectable with getargspec, so we'll if not func_accepts_kwargs(receiver):
# try a couple different ways but in the end fall back on assuming raise ValueError("Signal receivers must accept keyword arguments (**kwargs).")
# it is -- we don't want to prevent registration of valid but weird
# callables.
try:
argspec = inspect.getargspec(receiver)
except TypeError:
try:
argspec = inspect.getargspec(receiver.__call__)
except (TypeError, AttributeError):
argspec = None
if argspec:
assert argspec[2] is not None, \
"Signal receivers must accept keyword arguments (**kwargs)."
if dispatch_uid: if dispatch_uid:
lookup_key = (dispatch_uid, _make_id(sender)) lookup_key = (dispatch_uid, _make_id(sender))

View File

@ -51,10 +51,10 @@ u'<html></html>'
from __future__ import unicode_literals from __future__ import unicode_literals
import inspect
import logging import logging
import re import re
import warnings import warnings
from inspect import getargspec, getcallargs
from django.template.context import ( # NOQA: imported for backwards compatibility from django.template.context import ( # NOQA: imported for backwards compatibility
BaseContext, Context, ContextPopException, RequestContext, BaseContext, Context, ContextPopException, RequestContext,
@ -66,6 +66,7 @@ from django.utils.encoding import (
) )
from django.utils.formats import localize from django.utils.formats import localize
from django.utils.html import conditional_escape, escape from django.utils.html import conditional_escape, escape
from django.utils.inspect import getargspec
from django.utils.safestring import ( from django.utils.safestring import (
EscapeData, SafeData, mark_for_escaping, mark_safe, EscapeData, SafeData, mark_for_escaping, mark_safe,
) )
@ -723,7 +724,8 @@ class FilterExpression(object):
plen = len(provided) + 1 plen = len(provided) + 1
# Check to see if a decorator is providing the real function. # Check to see if a decorator is providing the real function.
func = getattr(func, '_decorated_function', func) func = getattr(func, '_decorated_function', func)
args, varargs, varkw, defaults = getargspec(func)
args, _, _, defaults = getargspec(func)
alen = len(args) alen = len(args)
dlen = len(defaults or []) dlen = len(defaults or [])
# Not enough OR Too many # Not enough OR Too many
@ -884,7 +886,7 @@ class Variable(object):
current = current() current = current()
except TypeError: except TypeError:
try: try:
getcallargs(current) inspect.getcallargs(current)
except TypeError: # arguments *were* required except TypeError: # arguments *were* required
current = context.template.engine.string_if_invalid # invalid method call current = context.template.engine.string_if_invalid # invalid method call
else: else:

View File

@ -1,10 +1,10 @@
import functools import functools
import warnings import warnings
from importlib import import_module from importlib import import_module
from inspect import getargspec
from django.utils import six from django.utils import six
from django.utils.deprecation import RemovedInDjango21Warning from django.utils.deprecation import RemovedInDjango21Warning
from django.utils.inspect import getargspec
from django.utils.itercompat import is_iterable from django.utils.itercompat import is_iterable
from .base import Node, Template, token_kwargs from .base import Node, Template, token_kwargs

View File

@ -1,8 +1,8 @@
import warnings import warnings
from inspect import getargspec
from django.template import Origin, Template, TemplateDoesNotExist from django.template import Origin, Template, TemplateDoesNotExist
from django.utils.deprecation import RemovedInDjango21Warning from django.utils.deprecation import RemovedInDjango21Warning
from django.utils.inspect import func_supports_parameter
class Loader(object): class Loader(object):
@ -28,7 +28,7 @@ class Loader(object):
args = [template_name] args = [template_name]
# RemovedInDjango21Warning: Add template_dirs for compatibility with # RemovedInDjango21Warning: Add template_dirs for compatibility with
# old loaders # old loaders
if 'template_dirs' in getargspec(self.get_template_sources)[0]: if func_supports_parameter(self.get_template_sources, 'template_dirs'):
args.append(template_dirs) args.append(template_dirs)
for origin in self.get_template_sources(*args): for origin in self.get_template_sources(*args):

View File

@ -5,11 +5,11 @@ to load templates from them in order, caching the result.
import hashlib import hashlib
import warnings import warnings
from inspect import getargspec
from django.template import Origin, Template, TemplateDoesNotExist from django.template import Origin, Template, TemplateDoesNotExist
from django.utils.deprecation import RemovedInDjango21Warning from django.utils.deprecation import RemovedInDjango21Warning
from django.utils.encoding import force_bytes from django.utils.encoding import force_bytes
from django.utils.inspect import func_supports_parameter
from .base import Loader as BaseLoader from .base import Loader as BaseLoader
@ -51,7 +51,7 @@ class Loader(BaseLoader):
args = [template_name] args = [template_name]
# RemovedInDjango21Warning: Add template_dirs for compatibility # RemovedInDjango21Warning: Add template_dirs for compatibility
# with old loaders # with old loaders
if 'template_dirs' in getargspec(loader.get_template_sources)[0]: if func_supports_parameter(loader.get_template_sources, 'template_dirs'):
args.append(template_dirs) args.append(template_dirs)
for origin in loader.get_template_sources(*args): for origin in loader.get_template_sources(*args):
yield origin yield origin

View File

@ -1,3 +1,5 @@
from __future__ import absolute_import
import inspect import inspect
import warnings import warnings

80
django/utils/inspect.py Normal file
View File

@ -0,0 +1,80 @@
from __future__ import absolute_import
import inspect
from django.utils import six
def getargspec(func):
if six.PY2:
return inspect.getargspec(func)
sig = inspect.signature(func)
args = [
p.name for p in sig.parameters.values()
if p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD
]
varargs = [
p.name for p in sig.parameters.values()
if p.kind == inspect.Parameter.VAR_POSITIONAL
]
varargs = varargs[0] if varargs else None
varkw = [
p.name for p in sig.parameters.values()
if p.kind == inspect.Parameter.VAR_KEYWORD
]
varkw = varkw[0] if varkw else None
defaults = [
p.default for p in sig.parameters.values()
if p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD and p.default is not p.empty
] or None
return args, varargs, varkw, defaults
def get_func_args(func):
if six.PY2:
argspec = inspect.getargspec(func)
return argspec.args[1:] # ignore 'self'
sig = inspect.signature(func)
return [
arg_name for arg_name, param in sig.parameters.items()
if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD
]
def func_accepts_kwargs(func):
if six.PY2:
# Not all callables are inspectable with getargspec, so we'll
# try a couple different ways but in the end fall back on assuming
# it is -- we don't want to prevent registration of valid but weird
# callables.
try:
argspec = inspect.getargspec(func)
except TypeError:
try:
argspec = inspect.getargspec(func.__call__)
except (TypeError, AttributeError):
argspec = None
return not argspec or argspec[2] is not None
return any(
p for p in inspect.signature(func).parameters.values()
if p.kind == p.VAR_KEYWORD
)
def func_has_no_args(func):
args = inspect.getargspec(func)[0] if six.PY2 else [
p for p in inspect.signature(func).parameters.values()
if p.kind == p.POSITIONAL_OR_KEYWORD and p.default is p.empty
]
return len(args) == 1
def func_supports_parameter(func, parameter):
if six.PY3:
return parameter in inspect.signature(func).parameters
else:
args, varargs, varkw, defaults = inspect.getargspec(func)
return parameter in args