tuxbot-bot/venv/lib/python3.7/site-packages/jishaku/repl/inspections.py
2019-12-16 18:12:10 +01:00

213 lines
4.7 KiB
Python

# -*- coding: utf-8 -*-
"""
jishaku.repl.inspections
~~~~~~~~~~~~~~~~~~~~~~~~~
Inspections performable on Python objects.
:copyright: (c) 2019 Devon (Gorialis) R
:license: MIT, see LICENSE for more details.
"""
import collections
import functools
import inspect
import os
INSPECTIONS = []
MethodWrapperType = type((1).__le__)
WrapperDescriptorType = type(int.__le__)
def add_inspection(name):
"""
Add a Jishaku object inspection
"""
# create the real decorator
def inspection_inner(func):
"""
Jishaku inspection decorator
"""
# pylint: disable=inconsistent-return-statements
# create an encapsulated version of the inspection that swallows exceptions
@functools.wraps(func)
def encapsulated(*args, **kwargs):
try:
return func(*args, **kwargs)
except (TypeError, AttributeError, ValueError, OSError):
return
INSPECTIONS.append((name, encapsulated))
return func
return inspection_inner
def all_inspections(obj):
"""
Generator to iterate all current Jishaku inspections.
"""
for name, callback in INSPECTIONS:
result = callback(obj)
if result:
yield name, result
def class_name(obj):
"""
Get the name of an object, including the module name if available.
"""
name = obj.__name__
module = getattr(obj, '__module__')
if module:
name = f'{module}.{name}'
return name
# pylint: disable=missing-docstring
# pylint: disable=inconsistent-return-statements
@add_inspection("Type")
def type_inspection(obj):
return type(obj).__name__
@add_inspection("Object ID")
def id_inspection(obj):
return hex(id(obj))
@add_inspection("Length")
def len_inspection(obj):
return len(obj)
@add_inspection("MRO")
def mro_inspection(obj):
if not inspect.isclass(obj):
return
return ', '.join(class_name(x) for x in inspect.getmro(obj))
@add_inspection("Type MRO")
def type_mro_inspection(obj):
obj_type = type(obj)
if obj_type in (type, object):
return
return ', '.join(class_name(x) for x in inspect.getmro(obj_type))
@add_inspection("Subclasses")
def subclass_inspection(obj):
if isinstance(obj, type) and hasattr(obj, "__subclasses__"):
subclasses = type.__subclasses__(obj)
else:
return
output = ', '.join(class_name(x) for x in subclasses[0:5])
if len(subclasses) > 5:
output += ', ...'
return output
@add_inspection("Module Name")
def module_inspection(obj):
return inspect.getmodule(obj).__name__
@add_inspection("File Location")
def file_loc_inspection(obj):
file_loc = inspect.getfile(obj)
cwd = os.getcwd()
if file_loc.startswith(cwd):
file_loc = "." + file_loc[len(cwd):]
return file_loc
@add_inspection("Line Span")
def line_span_inspection(obj):
source_lines, source_offset = inspect.getsourcelines(obj)
return f"{source_offset}-{source_offset + len(source_lines)}"
@add_inspection("Signature")
def sig_inspection(obj):
return inspect.signature(obj)
@add_inspection("Content Types")
def content_type_inspection(obj):
if not isinstance(obj, (tuple, list, set)):
return
total = len(obj)
types = collections.Counter(type(x) for x in obj)
output = ', '.join(f'{x.__name__} ({y*100/total:.1f}\uFF05)' for x, y in types.most_common(3))
if len(types) > 3:
output += ', ...'
return output
POSSIBLE_OPS = {
'<': 'lt',
'<=': 'le',
'==': 'eq',
'!=': 'ne',
'>': 'gt',
'>=': 'ge',
'+': 'add',
'-': 'sub',
'*': 'mul',
'@': 'matmul',
'/': 'truediv',
'//': 'floordiv',
'\uFF05': 'mod', # fake percent to avoid prolog comment
'**': 'pow',
'<<': 'lshift',
'>>': 'rshift',
'&': 'and',
'^': 'xor',
'|': 'or'
}
def check_not_slot(obj, attr):
"""
Check that a given attribute isn't just an open slot for subclasses
"""
if isinstance(getattr(obj, attr, None), MethodWrapperType):
return not isinstance(getattr(type(obj), attr, None), WrapperDescriptorType)
return not isinstance(getattr(obj, attr, None), WrapperDescriptorType)
@add_inspection("Operations")
def compat_operation_inspection(obj):
this_dict = dir(obj)
operations = []
for operation, member in POSSIBLE_OPS.items():
if f'__{member}__' in this_dict and check_not_slot(obj, f'__{member}__'):
operations.append(operation)
elif f'__r{member}__' in this_dict and check_not_slot(obj, f'r__{member}__'):
operations.append(operation)
if f'__i{member}__' in this_dict and check_not_slot(obj, f'i__{member}__'):
operations.append(f'{operation}=')
return ' '.join(operations)