summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Graham <timograham@gmail.com>2015-06-10 17:24:04 -0400
committerTim Graham <timograham@gmail.com>2015-10-25 06:02:21 -0400
commite2ea30c4403fca1b0a2032aae7a783f8705fc08b (patch)
tree3f4f39f8422db192425cdde82d1af1344df5fc22
parent84ec3bfc1111525ee0b21afff870840b2eb13771 (diff)
downloaddjango-e2ea30c4403fca1b0a2032aae7a783f8705fc08b.tar.gz
[1.8.x] Fixed #24979 -- Removed usage of inspect.getargspec().
Backport of 3872a33132a4bb6aa22b237927597bbfdf6f21d7 from master
-rw-r--r--django/contrib/admindocs/views.py3
-rw-r--r--django/contrib/gis/management/commands/ogrinspect.py4
-rw-r--r--django/core/files/storage.py5
-rw-r--r--django/db/migrations/writer.py13
-rw-r--r--django/db/models/fields/files.py5
-rw-r--r--django/db/utils.py12
-rw-r--r--django/dispatch/dispatcher.py18
-rw-r--r--django/template/base.py8
-rw-r--r--django/utils/deprecation.py2
-rw-r--r--django/utils/inspect.py80
10 files changed, 114 insertions, 36 deletions
diff --git a/django/contrib/admindocs/views.py b/django/contrib/admindocs/views.py
index 394c408c91..90d8f82931 100644
--- a/django/contrib/admindocs/views.py
+++ b/django/contrib/admindocs/views.py
@@ -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):
diff --git a/django/contrib/gis/management/commands/ogrinspect.py b/django/contrib/gis/management/commands/ogrinspect.py
index 1194cf6cfb..f161c06e89 100644
--- a/django/contrib/gis/management/commands/ogrinspect.py
+++ b/django/contrib/gis/management/commands/ogrinspect.py
@@ -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']:
diff --git a/django/core/files/storage.py b/django/core/files/storage.py
index e611d3ac7b..39cf5d800b 100644
--- a/django/core/files/storage.py
+++ b/django/core/files/storage.py
@@ -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(
diff --git a/django/db/migrations/writer.py b/django/db/migrations/writer.py
index 1bed59b3ef..779c842af3 100644
--- a/django/db/migrations/writer.py
+++ b/django/db/migrations/writer.py
@@ -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)
diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py
index 73c67bef81..d6c1eaf49f 100644
--- a/django/db/models/fields/files.py
+++ b/django/db/models/fields/files.py
@@ -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(
diff --git a/django/db/utils.py b/django/db/utils.py
index 2261a69f71..47dd5fd937 100644
--- a/django/db/utils.py
+++ b/django/db/utils.py
@@ -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 "
diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py
index b02adb8e80..5d7f24ce34 100644
--- a/django/dispatch/dispatcher.py
+++ b/django/dispatch/dispatcher.py
@@ -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))
diff --git a/django/template/base.py b/django/template/base.py
index 5661bdc000..79c8a482e8 100644
--- a/django/template/base.py
+++ b/django/template/base.py
@@ -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:
diff --git a/django/utils/deprecation.py b/django/utils/deprecation.py
index 6a0c91daa0..8a24764f44 100644
--- a/django/utils/deprecation.py
+++ b/django/utils/deprecation.py
@@ -1,3 +1,5 @@
+from __future__ import absolute_import
+
import inspect
import warnings
diff --git a/django/utils/inspect.py b/django/utils/inspect.py
new file mode 100644
index 0000000000..3e3ad0ac23
--- /dev/null
+++ b/django/utils/inspect.py
@@ -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