[1.8.x] Fixed #24979 -- Removed usage of inspect.getargspec().

Backport of 3872a33132 from master
This commit is contained in:
Tim Graham 2015-06-10 17:24:04 -04:00
parent 84ec3bfc11
commit e2ea30c440
10 changed files with 114 additions and 36 deletions

View File

@ -20,6 +20,7 @@ from django.template.engine import Engine
from django.utils import six
from django.utils._os import upath
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.views.generic import TemplateView
@ -247,7 +248,7 @@ class ModelDetailView(BaseAdminDocsView):
# Gather model methods.
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:
for exclude in MODEL_METHODS_EXCLUDE:
if func_name.startswith(exclude):

View File

@ -1,8 +1,8 @@
import argparse
import inspect
from django.contrib.gis import gdal
from django.core.management.base import BaseCommand, CommandError
from django.utils.inspect import get_func_args
class LayerOptionAction(argparse.Action):
@ -91,7 +91,7 @@ class Command(BaseCommand):
from django.contrib.gis.utils.ogrinspect import _ogrinspect, mapping
# Filter options to params accepted by `_ogrinspect`
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)]
if options['mapping']:

View File

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

View File

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

View File

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

View File

@ -333,8 +333,16 @@ class ConnectionRouter(object):
# If the router doesn't have a method, skip to the next one.
continue
argspec = inspect.getargspec(method)
if len(argspec.args) == 3 and not argspec.keywords:
if six.PY3:
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(
"The signature of allow_migrate has changed from "
"allow_migrate(self, db, model) to "

View File

@ -2,6 +2,7 @@ import sys
import threading
import weakref
from django.utils.inspect import func_accepts_kwargs
from django.utils.six.moves import range
if sys.version_info < (3, 4):
@ -87,24 +88,11 @@ class Signal(object):
# If DEBUG is on, check that we got a good receiver
if settings.configured and settings.DEBUG:
import inspect
assert callable(receiver), "Signal receivers must be callable."
# Check for **kwargs
# 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(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 not func_accepts_kwargs(receiver):
raise ValueError("Signal receivers must accept keyword arguments (**kwargs).")
if dispatch_uid:
lookup_key = (dispatch_uid, _make_id(sender))

View File

@ -51,11 +51,11 @@ u'<html></html>'
from __future__ import unicode_literals
import inspect
import re
import warnings
from functools import partial
from importlib import import_module
from inspect import getargspec, getcallargs
from django.apps import apps
from django.template.context import ( # NOQA: imported for backwards compatibility
@ -68,6 +68,7 @@ from django.utils.encoding import (
)
from django.utils.formats import localize
from django.utils.html import conditional_escape
from django.utils.inspect import getargspec
from django.utils.itercompat import is_iterable
from django.utils.module_loading import module_has_submodule
from django.utils.safestring import (
@ -686,7 +687,8 @@ class FilterExpression(object):
plen = len(provided) + 1
# Check to see if a decorator is providing the real function.
func = getattr(func, '_decorated_function', func)
args, varargs, varkw, defaults = getargspec(func)
args, _, _, defaults = getargspec(func)
alen = len(args)
dlen = len(defaults or [])
# Not enough OR Too many
@ -847,7 +849,7 @@ class Variable(object):
current = current()
except TypeError:
try:
getcallargs(current)
inspect.getcallargs(current)
except TypeError: # arguments *were* required
current = context.template.engine.string_if_invalid # invalid method call
else:

View File

@ -1,3 +1,5 @@
from __future__ import absolute_import
import inspect
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