text: # -*- coding: utf-8 -*-
  
  """
  jishaku.repl.inspections
  ~~~~~~~~~~~~~~~~~~~~~~~~~
  
  Inspections performable on Python objects.
  
  :copyright: (c) 2021 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: str):
      """
      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',
      '%': 'mod',
      '**': '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)
  