Skip to content
This repository was archived by the owner on Jan 10, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ celerybeat-schedule
.env
.venv
env/
env2/
env3/
venv/
ENV/
env.bak/
Expand Down
8 changes: 2 additions & 6 deletions pinject/bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,9 @@
"""


import inspect
import re
import inspect
import threading
import types

from .third_party import decorator

from . import binding_keys
from . import decorators
Expand Down Expand Up @@ -181,8 +178,7 @@ def get_provider_bindings(
get_arg_names_from_provider_fn_name=(
providing.default_get_arg_names_from_provider_fn_name)):
provider_bindings = []
fns = inspect.getmembers(binding_spec,
lambda x: type(x) == types.MethodType)
fns = inspect.getmembers(binding_spec, lambda x: inspect.ismethod(x))
for _, fn in fns:
default_arg_names = get_arg_names_from_provider_fn_name(fn.__name__)
fn_bindings = get_provider_fn_bindings(fn, default_arg_names)
Expand Down
34 changes: 14 additions & 20 deletions pinject/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,14 @@
"""


import collections
import inspect

from .third_party import decorator
import decorator

from . import arg_binding_keys
from . import binding_keys
from . import support
from . import errors
from . import locations
from . import scoping


_ARG_BINDING_KEYS_ATTR = '_pinject_arg_binding_keys'
_IS_WRAPPER_ATTR = '_pinject_is_wrapper'
_NON_INJECTABLE_ARG_NAMES_ATTR = '_pinject_non_injectables'
Expand Down Expand Up @@ -83,13 +79,12 @@ def inject(arg_names=None, all_except=None):
back_frame_loc = locations.get_back_frame_loc()
if arg_names is not None and all_except is not None:
raise errors.TooManyArgsToInjectDecoratorError(back_frame_loc)
for arg, arg_value in [('arg_names', arg_names),
('all_except', all_except)]:
for arg, arg_value in [('arg_names', arg_names), ('all_except', all_except)]:
if arg_value is not None:
if not arg_value:
raise errors.EmptySequenceArgError(back_frame_loc, arg)
if (not isinstance(arg_value, collections.Sequence) or
isinstance(arg_value, basestring)):
if (not support.is_sequence(arg_value) or
support.is_string(arg_value)):
raise errors.WrongArgTypeError(
arg, 'sequence (of arg names)', type(arg_value).__name__)
if arg_names is None and all_except is None:
Expand Down Expand Up @@ -207,6 +202,7 @@ def _get_pinject_decorated_fn(fn):
else:
def _pinject_decorated_fn(fn_to_wrap, *pargs, **kwargs):
return fn_to_wrap(*pargs, **kwargs)

pinject_decorated_fn = decorator.decorator(_pinject_decorated_fn, fn)
# TODO(kurts): split this so that __init__() decorators don't get
# the provider attribute.
Expand All @@ -225,28 +221,26 @@ def _get_pinject_wrapper(
def get_pinject_decorated_fn_with_additions(fn):
pinject_decorated_fn = _get_pinject_decorated_fn(fn)
orig_arg_names, unused_varargs, unused_keywords, unused_defaults = (
inspect.getargspec(getattr(pinject_decorated_fn, _ORIG_FN_ATTR)))
support.get_method_args(getattr(pinject_decorated_fn, _ORIG_FN_ATTR)))
if arg_binding_key is not None:
if not arg_binding_key.can_apply_to_one_of_arg_names(
orig_arg_names):
raise errors.NoSuchArgToInjectError(
decorator_loc, arg_binding_key, fn)
if not arg_binding_key.can_apply_to_one_of_arg_names(orig_arg_names):
raise errors.NoSuchArgToInjectError(decorator_loc, arg_binding_key, fn)
if arg_binding_key.conflicts_with_any_arg_binding_key(
getattr(pinject_decorated_fn, _ARG_BINDING_KEYS_ATTR)):
getattr(pinject_decorated_fn, _ARG_BINDING_KEYS_ATTR)):
raise errors.MultipleAnnotationsForSameArgError(
arg_binding_key, decorator_loc)
getattr(pinject_decorated_fn, _ARG_BINDING_KEYS_ATTR).append(
arg_binding_key)
if (provider_arg_name is not None or
provider_annotated_with is not None or
provider_in_scope_id is not None):
provider_in_scope_id is not None):
provider_decorations = getattr(
pinject_decorated_fn, _PROVIDER_DECORATIONS_ATTR)
provider_decorations.append(ProviderDecoration(
provider_arg_name, provider_annotated_with,
provider_in_scope_id))
if (inject_arg_names is not None or
inject_all_except_arg_names is not None):
inject_all_except_arg_names is not None):
if hasattr(pinject_decorated_fn, _NON_INJECTABLE_ARG_NAMES_ATTR):
raise errors.DuplicateDecoratorError('inject', decorator_loc)
non_injectable_arg_names = []
Expand All @@ -265,6 +259,7 @@ def get_pinject_decorated_fn_with_additions(fn):
if len(non_injectable_arg_names) == len(orig_arg_names):
raise errors.NoRemainingArgsToInjectError(decorator_loc)
return pinject_decorated_fn

return get_pinject_decorated_fn_with_additions


Expand All @@ -285,8 +280,7 @@ def get_injectable_arg_binding_keys(fn, direct_pargs, direct_kwargs):
existing_arg_binding_keys = []
orig_fn = fn

arg_names, unused_varargs, unused_keywords, defaults = (
inspect.getargspec(orig_fn))
arg_names, unused_varargs, unused_keywords, defaults = support.get_method_args(orig_fn)
num_args_with_defaults = len(defaults) if defaults is not None else 0
if num_args_with_defaults:
arg_names = arg_names[:-num_args_with_defaults]
Expand Down
2 changes: 1 addition & 1 deletion pinject/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"""


import locations
from . import locations


class Error(Exception):
Expand Down
5 changes: 2 additions & 3 deletions pinject/finding.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,10 @@ def find_classes(modules, classes):

def _get_explicit_or_default_modules(modules):
if modules is ALL_IMPORTED_MODULES:
return sys.modules.values()
return list(sys.modules.values())
elif modules is None:
return []
else:
return modules
return modules


def _find_classes_in_module(module):
Expand Down
11 changes: 6 additions & 5 deletions pinject/initializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@
"""


import inspect

from .third_party import decorator
import decorator

from . import errors
from . import support


def copy_args_to_internal_fields(fn):
Expand All @@ -42,14 +41,16 @@ def _copy_args_to_fields(fn, decorator_name, field_prefix):
raise errors.DecoratorAppliedToNonInitError(
decorator_name, fn)
arg_names, varargs, unused_keywords, unused_defaults = (
inspect.getargspec(fn))
support.get_method_args(fn))
if varargs is not None:
raise errors.PargsDisallowedWhenCopyingArgsError(
decorator_name, fn, varargs)

def CopyThenCall(fn_to_wrap, self, *pargs, **kwargs):
for index, parg in enumerate(pargs, start=1):
setattr(self, field_prefix + arg_names[index], parg)
for kwarg, kwvalue in kwargs.iteritems():
for kwarg, kwvalue in support.items(kwargs):
setattr(self, field_prefix + kwarg, kwvalue)
fn_to_wrap(self, *pargs, **kwargs)

return decorator.decorator(CopyThenCall, fn)
75 changes: 69 additions & 6 deletions pinject/locations.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

import inspect

LOCALS_TOKEN = '<locals>'


def get_loc(thing):
try:
Expand All @@ -27,12 +29,8 @@ def get_loc(thing):

def get_name_and_loc(thing):
try:
if hasattr(thing, 'im_class'):
class_name = '{0}.{1}'.format(
thing.im_class.__name__, thing.__name__)
else:
class_name = '{0}.{1}'.format(
inspect.getmodule(thing).__name__, thing.__name__)
type_name = _get_type_name(thing)
class_name = '{0}.{1}'.format(type_name, thing.__name__)
except (TypeError, IOError):
class_name = '{0}.{1}'.format(
inspect.getmodule(thing).__name__, thing.__name__)
Expand All @@ -47,3 +45,68 @@ def get_back_frame_loc():
back_frame = inspect.currentframe().f_back.f_back
return '{0}:{1}'.format(back_frame.f_code.co_filename,
back_frame.f_lineno)


def _get_type_name(target_thing):
"""
Functions, bound methods and unbound methods change significantly in Python 3.

For instance:

class SomeObject(object):
def method():
pass

In Python 2:
- Unbound method inspect.ismethod(SomeObject.method) returns True
- Unbound inspect.isfunction(SomeObject.method) returns False
- Unbound hasattr(SomeObject.method, 'im_class') returns True
- Bound method inspect.ismethod(SomeObject().method) returns True
- Bound method inspect.isfunction(SomeObject().method) returns False
- Bound hasattr(SomeObject().method, 'im_class') returns True

In Python 3:
- Unbound method inspect.ismethod(SomeObject.method) returns False
- Unbound inspect.isfunction(SomeObject.method) returns True
- Unbound hasattr(SomeObject.method, 'im_class') returns False
- Bound method inspect.ismethod(SomeObject().method) returns True
- Bound method inspect.isfunction(SomeObject().method) returns False
- Bound hasattr(SomeObject().method, 'im_class') returns False

This method tries to consolidate the approach for retrieving the
enclosing type of a bound/unbound method and functions.
"""
thing = target_thing
if hasattr(thing, 'im_class'):
# only works in Python 2
return thing.im_class.__name__
if inspect.ismethod(thing):
for cls in inspect.getmro(thing.__self__.__class__):
if cls.__dict__.get(thing.__name__) is thing:
return cls.__name__
thing = thing.__func__
if inspect.isfunction(thing) and hasattr(thing, '__qualname__'):
qualifier = thing.__qualname__
if LOCALS_TOKEN in qualifier:
return _get_local_type_name(thing)
return _get_external_type_name(thing)
return inspect.getmodule(target_thing).__name__


def _get_local_type_name(thing):
qualifier = thing.__qualname__
parts = qualifier.split(LOCALS_TOKEN, 1)
type_name = parts[1].split('.')[1]
if thing.__name__ == type_name:
return inspect.getmodule(thing).__name__
return type_name


def _get_external_type_name(thing):
qualifier = thing.__qualname__
name = qualifier.rsplit('.', 1)[0]
if hasattr(inspect.getmodule(thing), name):
cls = getattr(inspect.getmodule(thing), name)
if isinstance(cls, type):
return cls.__name__
return inspect.getmodule(thing).__name__
69 changes: 14 additions & 55 deletions pinject/object_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@
"""


import collections
import functools
import inspect
import types

from . import arg_binding_keys
from . import bindings
from . import decorators
from . import errors
Expand All @@ -30,6 +24,7 @@
from . import providing
from . import required_bindings as required_bindings_lib
from . import scoping
from . import support


def new_object_graph(
Expand Down Expand Up @@ -81,21 +76,21 @@ def new_object_graph(
"""
try:
if modules is not None and modules is not finding.ALL_IMPORTED_MODULES:
_verify_types(modules, types.ModuleType, 'modules')
support.verify_module_types(modules, 'modules')
if classes is not None:
_verify_types(classes, types.TypeType, 'classes')
support.verify_class_types(classes, 'classes')
if binding_specs is not None:
_verify_subclasses(
support.verify_subclasses(
binding_specs, bindings.BindingSpec, 'binding_specs')
if get_arg_names_from_class_name is not None:
_verify_callable(get_arg_names_from_class_name,
'get_arg_names_from_class_name')
support.verify_callable(get_arg_names_from_class_name,
'get_arg_names_from_class_name')
if get_arg_names_from_provider_fn_name is not None:
_verify_callable(get_arg_names_from_provider_fn_name,
'get_arg_names_from_provider_fn_name')
support.verify_callable(get_arg_names_from_provider_fn_name,
'get_arg_names_from_provider_fn_name')
if is_scope_usable_from_scope is not None:
_verify_callable(is_scope_usable_from_scope,
'is_scope_usable_from_scope')
support.verify_callable(is_scope_usable_from_scope,
'is_scope_usable_from_scope')
injection_context_factory = injection_contexts.InjectionContextFactory(
is_scope_usable_from_scope)
id_to_scope = scoping.get_id_to_scope_with_defaults(id_to_scope)
Expand Down Expand Up @@ -169,46 +164,10 @@ def new_object_graph(
use_short_stack_traces)


def _verify_type(elt, required_type, arg_name):
if type(elt) != required_type:
raise errors.WrongArgTypeError(
arg_name, required_type.__name__, type(elt).__name__)


def _verify_types(seq, required_type, arg_name):
if not isinstance(seq, collections.Sequence):
raise errors.WrongArgTypeError(
arg_name, 'sequence (of {0})'.format(required_type.__name__),
type(seq).__name__)
for idx, elt in enumerate(seq):
if type(elt) != required_type:
raise errors.WrongArgElementTypeError(
arg_name, idx, required_type.__name__, type(elt).__name__)


def _verify_subclasses(seq, required_superclass, arg_name):
if not isinstance(seq, collections.Sequence):
raise errors.WrongArgTypeError(
arg_name,
'sequence (of subclasses of {0})'.format(
required_superclass.__name__),
type(seq).__name__)
for idx, elt in enumerate(seq):
if not isinstance(elt, required_superclass):
raise errors.WrongArgElementTypeError(
arg_name, idx,
'subclass of {0}'.format(required_superclass.__name__),
type(elt).__name__)


def _verify_callable(fn, arg_name):
if not callable(fn):
raise errors.WrongArgTypeError(arg_name, 'callable', type(fn).__name__)


def _pare_to_present_args(kwargs, fn):
arg_names, _, _, _ = inspect.getargspec(fn)
return {arg: value for arg, value in kwargs.iteritems() if arg in arg_names}
arg_names, _, _, _ = support.get_method_args(fn)
return {arg: value
for arg, value in support.items(kwargs) if arg in arg_names}


class ObjectGraph(object):
Expand All @@ -231,7 +190,7 @@ def provide(self, cls):
Raises:
Error: an instance of cls is not providable
"""
_verify_type(cls, types.TypeType, 'cls')
support.verify_class_type(cls, 'cls')
if not self._is_injectable_fn(cls):
provide_loc = locations.get_back_frame_loc()
raise errors.NonExplicitlyBoundClassError(provide_loc, cls)
Expand Down
Loading