def is_async_fn(self, obj): if hasattr(obj, "__self__"): if isinstance( obj.__self__, asynq.decorators.AsyncDecorator ) or asynq.is_async_fn(obj.__self__): return True return asynq.is_async_fn(obj)
def is_impure_async_fn(value): """Returns whether the given Value is an impure async call. This can be used to detect places where async functions are called synchronously. """ if isinstance(value, KnownValue): return asynq.is_async_fn(value.val) and not asynq.is_pure_async_fn(value.val) elif isinstance(value, UnboundMethodValue): method = value.get_method() if method is None: return False return asynq.is_async_fn(method) and not asynq.is_pure_async_fn(method) return False
def _should_record_usage(self, value, include_modules=False): # also include async functions, but don't call is_async_fn on modules because it can fail if inspect.isfunction(value) or (not inspect.ismodule(value) and asynq.is_async_fn(value)): registered = self.config.registered_values() try: return value not in registered except TypeError: return False # mock.call can get here elif inspect.isclass(value): # can't reliably detect usage of classes with a metaclass metaclass = type(value) try: is_allowed = metaclass in self.config.ALLOWED_METACLASSES except TypeError: # apparently mock objects have a dictionary as their metaclass is_allowed = False if not is_allowed: return False # controllers are called directly by Pylons if any( issubclass(value, cls) for cls in self.config.USED_BASE_CLASSES): return False return value not in self.config.registered_values() elif include_modules and inspect.ismodule(value): return True else: return False
def _check_attribute_access_in_async(self, root_value, attr_name, node): if isinstance(root_value, TypedValue): if not ( hasattr(root_value.typ, attr_name) and isinstance(getattr(root_value.typ, attr_name), property) ): return async_names = ("get_" + attr_name, "is_" + attr_name) for async_name in async_names: if hasattr(root_value.typ, async_name) and asynq.is_async_fn( getattr(root_value.typ, async_name) ): replacement_call = _stringify_async_fn( UnboundMethodValue(async_name, root_value.typ, "asynq") ) method_node = ast.Attribute(value=node.value, attr=async_name) func_node = ast.Attribute(value=method_node, attr="asynq") kwargs = {"args": [], "keywords": []} if six.PY2: kwargs["starargs"] = kwargs["kwargs"] = None call_node = ast.Call(func=func_node, **kwargs) replacement_node = ast.Yield(value=call_node) self._show_impure_async_error( node, replacement_call=replacement_call, replacement_node=replacement_node, )
def _unwrap_value_from_typed(result: Value, typ: type, ctx: AttrContext) -> Value: if not isinstance(result, KnownValue): return result cls_val = result.val if isinstance(cls_val, property): typ = ctx.get_property_type_from_config(cls_val) if typ is not UNRESOLVED_VALUE: return typ return ctx.get_property_type_from_argspec(cls_val) elif qcore.inspection.is_classmethod(cls_val): return KnownValue(cls_val) elif inspect.ismethod(cls_val): return UnboundMethodValue(ctx.attr, ctx.root_value) elif inspect.isfunction(cls_val): # either a staticmethod or an unbound method try: descriptor = inspect.getattr_static(typ, ctx.attr) except AttributeError: # probably a super call; assume unbound method if ctx.attr != "__new__": return UnboundMethodValue(ctx.attr, ctx.root_value) else: # __new__ is implicitly a staticmethod return KnownValue(cls_val) if isinstance(descriptor, staticmethod) or ctx.attr == "__new__": return KnownValue(cls_val) else: return UnboundMethodValue(ctx.attr, ctx.root_value) elif isinstance(cls_val, (MethodDescriptorType, SlotWrapperType)): # built-in method; e.g. scope_lib.tests.SimpleDatabox.get return UnboundMethodValue(ctx.attr, ctx.root_value) elif (_static_hasattr(cls_val, "decorator") and _static_hasattr(cls_val, "instance") and not isinstance(cls_val.instance, type)): # non-static method return UnboundMethodValue(ctx.attr, ctx.root_value) elif asynq.is_async_fn(cls_val): # static or class method return KnownValue(cls_val) elif _static_hasattr(cls_val, "__get__"): return ctx.get_property_type_from_config(cls_val) elif ctx.should_ignore_class_attribute(cls_val): return UNRESOLVED_VALUE else: return KnownValue(cls_val)
def _should_record_as_unused(self, module, attr, value): if self.config.should_ignore_unused(module, attr, value): return False # TODO: remove most of the below and just rely on @used and # should_ignore_unused() # also include async functions, but don't call is_async_fn on modules because it can fail if inspect.isfunction(value) or ( not inspect.ismodule(value) and asynq.is_async_fn(value) ): registered = self.config.registered_values() try: if value in registered: return False except TypeError: return False # mock.call can get here elif inspect.isclass(value): # can't reliably detect usage of classes with a metaclass metaclass = type(value) try: is_allowed = metaclass in self.config.ALLOWED_METACLASSES except TypeError: # apparently mock objects have a dictionary as their metaclass is_allowed = False if not is_allowed: return False # controllers are called directly by Pylons if any(issubclass(value, cls) for cls in self.config.USED_BASE_CLASSES): return False if value in self.config.registered_values(): return False elif inspect.ismodule(value): # test modules will usually show up as unused if value.__name__.split(".")[-1].startswith("test"): return False # if it was ever import *ed from, don't treat it as unused if value in self.import_stars: return False try: # __future__ imports are usually unused return value not in _used_objects and not isinstance( value, __future__._Feature ) except Exception: return True
def _unwrap_value_from_subclass(result: Value, ctx: AttrContext) -> Value: if not isinstance(result, KnownValue): return result cls_val = result.val if (qcore.inspection.is_classmethod(cls_val) or inspect.ismethod(cls_val) or inspect.isfunction(cls_val) or isinstance(cls_val, (MethodDescriptorType, SlotWrapperType)) or ( # non-static method _static_hasattr(cls_val, "decorator") and _static_hasattr(cls_val, "instance") and not isinstance(cls_val.instance, type)) or asynq.is_async_fn(cls_val)): # static or class method return KnownValue(cls_val) elif _static_hasattr(cls_val, "__get__"): return UNRESOLVED_VALUE # can't figure out what this will return elif ctx.should_ignore_class_attribute(cls_val): return UNRESOLVED_VALUE else: return KnownValue(cls_val)
def is_dot_asynq_function(obj: Any) -> bool: """Returns whether obj is the .asynq member on an async function.""" try: self_obj = obj.__self__ except AttributeError: # the attribute doesn't exist return False except Exception: # The object has a buggy __getattr__ that threw an error. Just ignore it. return False if qcore.inspection.is_classmethod(obj): return False if obj is self_obj: return False try: is_async_fn = asynq.is_async_fn(self_obj) except Exception: # The object may have a buggy __getattr__. Ignore it. This happens with # pylons request objects. return False if not is_async_fn: return False return getattr(obj, "__name__", None) in ("async", "asynq")