text: import os
  import warnings
  from typing import Dict, Tuple
  
  from . import mgl
  from .buffer import Buffer
  from .compute_shader import ComputeShader
  from .conditional_render import ConditionalRender
  from .framebuffer import Framebuffer
  from .program import Program, detect_format
  from .program_members import (Attribute, Subroutine, Uniform, UniformBlock,
                                Varying)
  from .query import Query
  from .renderbuffer import Renderbuffer
  from .scope import Scope
  from .texture import Texture
  from .texture_3d import Texture3D
  from .texture_array import TextureArray
  from .texture_cube import TextureCube
  from .vertex_array import VertexArray
  from .sampler import Sampler
  
  __all__ = ['Context', 'create_context', 'create_standalone_context',
             'NOTHING', 'BLEND', 'DEPTH_TEST', 'CULL_FACE', 'RASTERIZER_DISCARD',
             'ZERO', 'ONE', 'SRC_COLOR', 'ONE_MINUS_SRC_COLOR', 'SRC_ALPHA', 'ONE_MINUS_SRC_ALPHA', 'DST_ALPHA',
             'ONE_MINUS_DST_ALPHA', 'DST_COLOR', 'ONE_MINUS_DST_COLOR',
             'DEFAULT_BLENDING', 'PREMULTIPLIED_ALPHA',
             'Error']
  
  
  Error = mgl.Error
  
  
  NOTHING = 0
  BLEND = 1
  DEPTH_TEST = 2
  CULL_FACE = 4
  RASTERIZER_DISCARD = 8
  
  
  ZERO = 0x0000
  ONE = 0x0001
  SRC_COLOR = 0x0300
  ONE_MINUS_SRC_COLOR = 0x0301
  SRC_ALPHA = 0x0302
  ONE_MINUS_SRC_ALPHA = 0x0303
  DST_ALPHA = 0x0304
  ONE_MINUS_DST_ALPHA = 0x0305
  DST_COLOR = 0x0306
  ONE_MINUS_DST_COLOR = 0x0307
  
  
  DEFAULT_BLENDING = (SRC_ALPHA, ONE_MINUS_SRC_ALPHA)
  PREMULTIPLIED_ALPHA = (SRC_ALPHA, ONE)
  
  
  class Context:
      '''
          Class exposing OpenGL features.
          ModernGL objects can be created from this class.
      '''
  
      __slots__ = ['mglo', '_screen', '_info', 'version_code', 'fbo', 'extra']
  
      def __init__(self):
          self.mglo = None
          self._screen = None
          self._info = None
          self.version_code = None  #: int: The OpenGL version code. Reports ``410`` for OpenGL 4.1
          self.fbo = None  #: Framebuffer: The active framebuffer. Set every time ``Framebuffer.use()`` is called.
          self.extra = None
          raise TypeError()
  
      def __repr__(self):
          return '<Context>'
  
      def __eq__(self, other):
          return type(self) is type(other) and self.mglo is other.mglo
  
      @property
      def line_width(self) -> float:
          '''
              float: Set the default line width.
          '''
  
          return self.mglo.line_width
  
      @line_width.setter
      def line_width(self, value):
          self.mglo.line_width = value
  
      @property
      def point_size(self) -> float:
          '''
              float: Set/get the default point size.
          '''
  
          return self.mglo.point_size
  
      @point_size.setter
      def point_size(self, value):
          self.mglo.point_size = value
  
      @property
      def depth_func(self) -> str:
          '''
              int: Set the default depth func.
              The depth function is set using a string.
  
              Example::
  
                  ctx.depth_func = '<='  # GL_LEQUAL
                  ctx.depth_func = '<'   # GL_LESS
                  ctx.depth_func = '>='  # GL_GEQUAL
                  ctx.depth_func = '>'   # GL_GREATER
                  ctx.depth_func = '=='  # GL_EQUAL 
                  ctx.depth_func = '!='  # GL_NOTEQUAL 
                  ctx.depth_func = '0'   # GL_NEVER 
                  ctx.depth_func = '1'   # GL_ALWAYS 
          '''
  
          raise NotImplementedError()
  
      @depth_func.setter
      def depth_func(self, value):
          self.mglo.depth_func = value
  
      @property
      def blend_func(self) -> Tuple[int, int]:
          '''
              tuple: Set the blend depth func.
  
              Example::
  
                  ctx.enable(moderngl.BLEND)
                  ctx.blend_func = moderngl.SRC_ALPHA, moderngl.ONE_MINUS_SRC_ALPHA
          '''
  
          raise NotImplementedError()
  
      @blend_func.setter
      def blend_func(self, value):
          self.mglo.blend_func = tuple(value)
  
      @property
      def multisample(self) -> bool:
          '''
              bool: Enable/disable multisample mode (``GL_MULTISAMPLE``).
              This property is write only.
  
              Example::
  
                  # Enable
                  ctx.multisample = True
                  # Disable
                  ctx.multisample = False
          '''
  
          raise NotImplementedError()
  
      @multisample.setter
      def multisample(self, value):
          self.mglo.multisample = value
  
      @property
      def viewport(self) -> Tuple[int, int, int, int]:
          '''
              tuple: The viewport of the active framebuffer.
              Modifies or gets the viewport.
  
              Example::
  
                  >>> ctx.viewport
                  (0, 0, 1280, 720)
                  >>> ctx.viewport = (0, 0, 640, 360)
                  >>> ctx.viewport
                  (0, 0, 640, 360)
  
              If no framebuffer is bound ``(0, 0, 0, 0)`` will be returned.
          '''
  
          return self.mglo.fbo.viewport
  
      @viewport.setter
      def viewport(self, value):
          self.mglo.fbo.viewport = tuple(value)
  
      @property
      def max_samples(self) -> int:
          '''
              int: The maximum supported number of samples for multisampling
          '''
  
          return self.mglo.max_samples
  
      @property
      def max_integer_samples(self) -> int:
          '''
              int: The max integer samples.
          '''
  
          return self.mglo.max_integer_samples
  
      @property
      def max_texture_units(self) -> int:
          '''
              int: The max texture units.
          '''
  
          return self.mglo.max_texture_units
  
      @property
      def default_texture_unit(self) -> int:
          '''
              int: The default texture unit.
          '''
  
          return self.mglo.default_texture_unit
  
      @default_texture_unit.setter
      def default_texture_unit(self, value):
          self.mglo.default_texture_unit = value
  
      @property
      def max_anisotropy(self):
          '''
              float: The maximum value supported for anisotropic filtering.
          '''
          return self.mglo.max_anisotropy
  
      @property
      def screen(self) -> 'Framebuffer':
          '''
              Framebuffer: A Framebuffer instance representing the screen usually
              set when creating a context with ``create_context()`` attaching to
              an existing context. This is the special system framebuffer
              represented by framebuffer ``id=0``.
  
              When creating a standalone context this property is not set.
          '''
  
          return self._screen
  
      @property
      def wireframe(self) -> bool:
          '''
              bool: Wireframe settings for debugging.
          '''
  
          return self.mglo.wireframe
  
      @wireframe.setter
      def wireframe(self, value):
          self.mglo.wireframe = value
  
      @property
      def front_face(self) -> str:
          '''
              str: The front_face. Acceptable values are ``'ccw'`` (default) or ``'cw'``.
  
              Face culling must be enabled for this to have any effect:
              ``ctx.enable(moderngl.CULL_FACE)``.
  
              Example::
  
                  # Triangles winded counter-clockwise considerd front facing
                  ctx.front_face = 'ccw'
                  # Triangles winded clockwise considerd front facing
                  ctx.front_face = 'cw'
          '''
  
          return self.mglo.front_face
  
      @front_face.setter
      def front_face(self, value):
          self.mglo.front_face = str(value)
  
      @property
      def patch_vertices(self) -> int:
          '''
              int: The number of vertices that will be used to make up a single patch
                   primitive.
          '''
  
          return self.mglo.patch_vertices
  
      @patch_vertices.setter
      def patch_vertices(self, value):
          self.mglo.patch_vertices = value
  
      @property
      def error(self) -> str:
          '''
              str: The result of ``glGetError()`` but human readable.
              This values is provided for debug purposes only and is likely to
              reduce performace when used in a draw loop.
          '''
  
          return self.mglo.error
  
      @property
      def info(self) -> Dict[str, object]:
          '''
              dict: Information about the context
  
              Example::
  
                  {
                      'GL_VENDOR': 'NVIDIA Corporation', 
                      'GL_RENDERER': 'NVIDIA GeForce GT 650M OpenGL Engine', 
                      'GL_VERSION': '4.1 NVIDIA-10.32.0 355.11.10.10.40.102', 
                      'GL_POINT_SIZE_RANGE': (1.0, 2047.0), 
                      'GL_SMOOTH_LINE_WIDTH_RANGE': (0.5, 1.0), 
                      'GL_ALIASED_LINE_WIDTH_RANGE': (1.0, 1.0), 
                      'GL_POINT_FADE_THRESHOLD_SIZE': 1.0, 
                      'GL_POINT_SIZE_GRANULARITY': 0.125, 
                      'GL_SMOOTH_LINE_WIDTH_GRANULARITY': 0.125, 
                      'GL_MIN_PROGRAM_TEXEL_OFFSET': -8.0, 
                      'GL_MAX_PROGRAM_TEXEL_OFFSET': 7.0, 
                      'GL_MINOR_VERSION': 1, 
                      'GL_MAJOR_VERSION': 4, 
                      'GL_SAMPLE_BUFFERS': 0, 
                      'GL_SUBPIXEL_BITS': 8, 
                      'GL_CONTEXT_PROFILE_MASK': 1, 
                      'GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT': 256, 
                      'GL_DOUBLEBUFFER': False, 
                      'GL_STEREO': False, 
                      'GL_MAX_VIEWPORT_DIMS': (16384, 16384), 
                      'GL_MAX_3D_TEXTURE_SIZE': 2048, 
                      'GL_MAX_ARRAY_TEXTURE_LAYERS': 2048, 
                      'GL_MAX_CLIP_DISTANCES': 8, 
                      'GL_MAX_COLOR_ATTACHMENTS': 8, 
                      'GL_MAX_COLOR_TEXTURE_SAMPLES': 8, 
                      'GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS': 233472, 
                      'GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS': 231424, 
                      'GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS': 80, 
                      'GL_MAX_COMBINED_UNIFORM_BLOCKS': 70, 
                      'GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS': 233472, 
                      'GL_MAX_CUBE_MAP_TEXTURE_SIZE': 16384, 
                      'GL_MAX_DEPTH_TEXTURE_SAMPLES': 8, 
                      'GL_MAX_DRAW_BUFFERS': 8, 
                      'GL_MAX_DUAL_SOURCE_DRAW_BUFFERS': 1, 
                      'GL_MAX_ELEMENTS_INDICES': 150000, 
                      'GL_MAX_ELEMENTS_VERTICES': 1048575, 
                      'GL_MAX_FRAGMENT_INPUT_COMPONENTS': 128, 
                      'GL_MAX_FRAGMENT_UNIFORM_COMPONENTS': 4096, 
                      'GL_MAX_FRAGMENT_UNIFORM_VECTORS': 1024, 
                      'GL_MAX_FRAGMENT_UNIFORM_BLOCKS': 14, 
                      'GL_MAX_GEOMETRY_INPUT_COMPONENTS': 128, 
                      'GL_MAX_GEOMETRY_OUTPUT_COMPONENTS': 128, 
                      'GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS': 16, 
                      'GL_MAX_GEOMETRY_UNIFORM_BLOCKS': 14, 
                      'GL_MAX_GEOMETRY_UNIFORM_COMPONENTS': 2048, 
                      'GL_MAX_INTEGER_SAMPLES': 1, 
                      'GL_MAX_SAMPLES': 8, 
                      'GL_MAX_RECTANGLE_TEXTURE_SIZE': 16384, 
                      'GL_MAX_RENDERBUFFER_SIZE': 16384, 
                      'GL_MAX_SAMPLE_MASK_WORDS': 1, 
                      'GL_MAX_SERVER_WAIT_TIMEOUT': -1, 
                      'GL_MAX_TEXTURE_BUFFER_SIZE': 134217728, 
                      'GL_MAX_TEXTURE_IMAGE_UNITS': 16, 
                      'GL_MAX_TEXTURE_LOD_BIAS': 15, 
                      'GL_MAX_TEXTURE_SIZE': 16384, 
                      'GL_MAX_UNIFORM_BUFFER_BINDINGS': 70, 
                      'GL_MAX_UNIFORM_BLOCK_SIZE': 65536, 
                      'GL_MAX_VARYING_COMPONENTS': 0, 
                      'GL_MAX_VARYING_VECTORS': 31, 
                      'GL_MAX_VARYING_FLOATS': 0, 
                      'GL_MAX_VERTEX_ATTRIBS': 16, 
                      'GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS': 16, 
                      'GL_MAX_VERTEX_UNIFORM_COMPONENTS': 4096, 
                      'GL_MAX_VERTEX_UNIFORM_VECTORS': 1024, 
                      'GL_MAX_VERTEX_OUTPUT_COMPONENTS': 128, 
                      'GL_MAX_VERTEX_UNIFORM_BLOCKS': 14, 
                      'GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET': 0, 
                      'GL_MAX_VERTEX_ATTRIB_BINDINGS': 0, 
                      'GL_VIEWPORT_BOUNDS_RANGE': (-32768, 32768), 
                      'GL_VIEWPORT_SUBPIXEL_BITS': 0, 
                      'GL_MAX_VIEWPORTS': 16
                  }
          '''
  
          if self._info is None:
              self._info = self.mglo.info
  
          return self._info
  
      def clear(self, red=0.0, green=0.0, blue=0.0, alpha=0.0, depth=1.0, *, viewport=None) -> None:
          '''
              Clear the bound framebuffer. By default clears the :py:data:`screen`.
  
              If the `viewport` is not ``None`` then scrissor test
              will be used to clear the given viewport.
  
              If the `viewport` is a 2-tuple it will clear the
              ``(0, 0, width, height)`` where ``(width, height)`` is the 2-tuple.
  
              If the `viewport` is a 4-tuple it will clear the given viewport.
  
              Args:
                  red (float): color component.
                  green (float): color component.
                  blue (float): color component.
                  alpha (float): alpha component.
                  depth (float): depth value.
  
              Keyword Args:
                  viewport (tuple): The viewport.
          '''
  
          self.mglo.fbo.clear(red, green, blue, alpha, depth, viewport)
  
      def enable_only(self, flags) -> None:
          '''
              Enable flags.
  
              - :py:data:`moderngl.NOTHING`
              - :py:data:`moderngl.BLEND`
              - :py:data:`moderngl.DEPTH_TEST`
              - :py:data:`moderngl.CULL_FACE`
              - :py:data:`moderngl.RASTERIZER_DISCARD`
  
              Args:
                  flags (EnableFlag): The flags to enable. Unset flags will be disabled.
          '''
  
          self.mglo.enable_only(flags)
  
      def enable(self, flags) -> None:
          '''
              Enable flags.
  
              For valid flags, please see :py:meth:`enable_only`.
  
              Args:
                  flag (int): The flags to enable.
          '''
  
          self.mglo.enable(flags)
  
      def disable(self, flags) -> None:
          '''
              Disable flags.
  
              For valid flags, please see :py:meth:`enable_only`.
  
              Args:
                  flag (int): The flags to disable.
          '''
  
          self.mglo.disable(flags)
  
      def finish(self) -> None:
          '''
              Wait for all drawing commands to finish.
          '''
  
          self.mglo.finish()
  
      def copy_buffer(self, dst, src, size=-1, *, read_offset=0, write_offset=0) -> None:
          '''
              Copy buffer content.
  
              Args:
                  dst (Buffer): The destination buffer.
                  src (Buffer): The source buffer.
                  size (int): The number of bytes to copy.
  
              Keyword Args:
                  read_offset (int): The read offset.
                  write_offset (int): The write offset.
          '''
  
          self.mglo.copy_buffer(dst.mglo, src.mglo, size, read_offset, write_offset)
  
      def copy_framebuffer(self, dst, src) -> None:
          '''
              Copy framebuffer content.
  
              Use this method to:
  
                  - blit framebuffers.
                  - copy framebuffer content into a texture.
                  - downsample framebuffers. (it will allow to read the framebuffer's content)
                  - downsample a framebuffer directly to a texture.
  
              Args:
                  dst (Framebuffer or Texture): Destination framebuffer or texture.
                  src (Framebuffer): Source framebuffer.
          '''
  
          self.mglo.copy_framebuffer(dst.mglo, src.mglo)
  
      def detect_framebuffer(self, glo=None) -> 'Framebuffer':
          '''
              Detect framebuffer.
  
              Args:
                  glo (int): Framebuffer object.
  
              Returns:
                  :py:class:`Framebuffer` object
          '''
  
          res = Framebuffer.__new__(Framebuffer)
          res.mglo, res._size, res._samples, res._glo = self.mglo.detect_framebuffer(glo)
          res._color_attachments = None
          res._depth_attachment = None
          res.ctx = self
          return res
  
      def buffer(self, data=None, *, reserve=0, dynamic=False) -> Buffer:
          '''
              Create a :py:class:`Buffer` object.
  
              Args:
                  data (bytes): Content of the new buffer.
  
              Keyword Args:
                  reserve (int): The number of bytes to reserve.
                  dynamic (bool): Treat buffer as dynamic.
  
              Returns:
                  :py:class:`Buffer` object
          '''
  
          if type(reserve) is str:
              reserve = mgl.strsize(reserve)
  
          res = Buffer.__new__(Buffer)
          res.mglo, res._size, res._glo = self.mglo.buffer(data, reserve, dynamic)
          res._dynamic = dynamic
          res.ctx = self
          return res
  
      def texture(self, size, components, data=None, *, samples=0, alignment=1, dtype='f1') -> 'Texture':
          '''
              Create a :py:class:`Texture` object.
  
              Args:
                  size (tuple): The width and height of the texture.
                  components (int): The number of components 1, 2, 3 or 4.
                  data (bytes): Content of the texture.
  
              Keyword Args:
                  samples (int): The number of samples. Value 0 means no multisample format.
                  alignment (int): The byte alignment 1, 2, 4 or 8.
                  dtype (str): Data type.
  
              Returns:
                  :py:class:`Texture` object
          '''
  
          res = Texture.__new__(Texture)
          res.mglo, res._glo = self.mglo.texture(size, components, data, samples, alignment, dtype)
          res._size = size
          res._components = components
          res._samples = samples
          res._dtype = dtype
          res._depth = False
          res.ctx = self
          return res
  
      def texture_array(self, size, components, data=None, *, alignment=1, dtype='f1') -> 'TextureArray':
          '''
              Create a :py:class:`TextureArray` object.
  
              Args:
                  size (tuple): The ``(width, height, layers)`` of the texture.
                  components (int): The number of components 1, 2, 3 or 4.
                  data (bytes): Content of the texture. The size must be ``(width, height * layers)``
                                so each layer is stacked vertically.
  
              Keyword Args:
                  alignment (int): The byte alignment 1, 2, 4 or 8.
                  dtype (str): Data type.
  
              Returns:
                  :py:class:`Texture3D` object
          '''
  
          res = TextureArray.__new__(TextureArray)
          res.mglo, res._glo = self.mglo.texture_array(size, components, data, alignment, dtype)
          res._size = size
          res._components = components
          res._dtype = dtype
          res.ctx = self
          return res
  
      def texture3d(self, size, components, data=None, *, alignment=1, dtype='f1') -> 'Texture3D':
          '''
              Create a :py:class:`Texture3D` object.
  
              Args:
                  size (tuple): The width, height and depth of the texture.
                  components (int): The number of components 1, 2, 3 or 4.
                  data (bytes): Content of the texture.
  
              Keyword Args:
                  alignment (int): The byte alignment 1, 2, 4 or 8.
                  dtype (str): Data type.
  
              Returns:
                  :py:class:`Texture3D` object
          '''
  
          res = Texture3D.__new__(Texture3D)
          res.mglo, res._glo = self.mglo.texture3d(size, components, data, alignment, dtype)
          res.ctx = self
          return res
  
      def texture_cube(self, size, components, data=None, *, alignment=1, dtype='f1') -> 'TextureCube':
          '''
              Create a :py:class:`TextureCube` object.
  
              Args:
                  size (tuple): The width, height of the texture. Each side of the cube will have this size.
                  components (int): The number of components 1, 2, 3 or 4.
                  data (bytes): Content of the texture.
  
              Keyword Args:
                  alignment (int): The byte alignment 1, 2, 4 or 8.
                  dtype (str): Data type.
  
              Returns:
                  :py:class:`TextureCube` object
          '''
  
          res = TextureCube.__new__(TextureCube)
          res.mglo, res._glo = self.mglo.texture_cube(size, components, data, alignment, dtype)
          res._size = size
          res._components = components
          res._dtype = dtype
          res.ctx = self
          return res
  
      def depth_texture(self, size, data=None, *, samples=0, alignment=4) -> 'Texture':
          '''
              Create a :py:class:`Texture` object.
  
              Args:
                  size (tuple): The width and height of the texture.
                  data (bytes): Content of the texture.
  
              Keyword Args:
                  samples (int): The number of samples. Value 0 means no multisample format.
                  alignment (int): The byte alignment 1, 2, 4 or 8.
  
              Returns:
                  :py:class:`Texture` object
          '''
  
          res = Texture.__new__(Texture)
          res.mglo, res._glo = self.mglo.depth_texture(size, data, samples, alignment)
          res._size = size
          res._components = 1
          res._samples = samples
          res._dtype = 'f4'
          res._depth = True
          res.ctx = self
          return res
  
      def vertex_array(self, program, content,
                       index_buffer=None, index_element_size=4, *, skip_errors=False) -> 'VertexArray':
          '''
              Create a :py:class:`VertexArray` object.
  
              Args:
                  program (Program): The program used when rendering.
                  content (list): A list of (buffer, format, attributes).
                  index_buffer (Buffer): An index buffer.
  
              Keyword Args:
                  index_element_size (int): byte size of each index element, 1, 2 or 4.
                  skip_errors (bool): Ignore skip_errors varyings.
  
              Returns:
                  :py:class:`VertexArray` object
          '''
          members = program._members
          index_buffer_mglo = None if index_buffer is None else index_buffer.mglo
          content = tuple((a.mglo, b) + tuple(getattr(members.get(x), 'mglo', None) for x in c) for a, b, *c in content)
  
          res = VertexArray.__new__(VertexArray)
          res.mglo, res._glo = self.mglo.vertex_array(program.mglo, content, index_buffer_mglo,
                                                      index_element_size, skip_errors)
          res._program = program
          res._index_buffer = index_buffer
          res._index_element_size = index_element_size
          res.ctx = self
          return res
  
      def simple_vertex_array(self, program, buffer, *attributes,
                              index_buffer=None, index_element_size=4) -> 'VertexArray':
          '''
              Create a :py:class:`VertexArray` object.
  
              Args:
                  program (Program): The program used when rendering.
                  buffer (Buffer): The buffer.
                  attributes (list): A list of attribute names.
  
              Keyword Args:
                  index_element_size (int): byte size of each index element, 1, 2 or 4.
                  index_buffer (Buffer): An index buffer.
  
              Returns:
                  :py:class:`VertexArray` object
          '''
  
          if type(buffer) is list:
              raise SyntaxError('Change simple_vertex_array to vertex_array')
  
          content = [(buffer, detect_format(program, attributes)) + attributes]
          return self.vertex_array(program, content, index_buffer, index_element_size)
  
      def program(self, *, vertex_shader, fragment_shader=None, geometry_shader=None,
                  tess_control_shader=None, tess_evaluation_shader=None, varyings=()) -> 'Program':
          '''
              Create a :py:class:`Program` object.
  
              Only linked programs will be returned.
  
              A single shader in the `shaders` parameter is also accepted.
              The varyings are only used when a transform program is created.
  
              Args:
                  shaders (list): A list of :py:class:`Shader` objects.
                  varyings (list): A list of varying names.
  
              Returns:
                  :py:class:`Program` object
          '''
  
          if type(varyings) is str:
              varyings = (varyings,)
  
          varyings = tuple(varyings)
  
          res = Program.__new__(Program)
          res.mglo, ls1, ls2, ls3, ls4, ls5, res._subroutines, res._geom, res._glo = self.mglo.program(
              vertex_shader, fragment_shader, geometry_shader, tess_control_shader, tess_evaluation_shader,
              varyings
          )
  
          members = {}
  
          for item in ls1:
              obj = Attribute.__new__(Attribute)
              obj.mglo, obj._location, obj._array_length, obj._dimension, obj._shape, obj._name = item
              members[obj.name] = obj
  
          for item in ls2:
              obj = Varying.__new__(Varying)
              obj._number, obj._array_length, obj._dimension, obj._name = item
              members[obj.name] = obj
  
          for item in ls3:
              obj = Uniform.__new__(Uniform)
              obj.mglo, obj._location, obj._array_length, obj._dimension, obj._name = item
              members[obj.name] = obj
  
          for item in ls4:
              obj = UniformBlock.__new__(UniformBlock)
              obj.mglo, obj._index, obj._size, obj._name = item
              members[obj.name] = obj
  
          for item in ls5:
              obj = Subroutine.__new__(Subroutine)
              obj._index, obj._name = item
              members[obj.name] = obj
  
          res._members = members
          res.ctx = self
          return res
  
      def query(self, *, samples=False, any_samples=False, time=False, primitives=False) -> 'Query':
          '''
              Create a :py:class:`Query` object.
  
              Keyword Args:
                  samples (bool): Query ``GL_SAMPLES_PASSED`` or not.
                  any_samples (bool): Query ``GL_ANY_SAMPLES_PASSED`` or not.
                  time (bool): Query ``GL_TIME_ELAPSED`` or not.
                  primitives (bool): Query ``GL_PRIMITIVES_GENERATED`` or not.
          '''
  
          res = Query.__new__(Query)
          res.mglo = self.mglo.query(samples, any_samples, time, primitives)
          res.crender = None
  
          if samples or any_samples:
              res.crender = ConditionalRender.__new__(ConditionalRender)
              res.crender.mglo = res.mglo
  
          res.ctx = self
          return res
  
      def scope(self, framebuffer, enable_only=None, *, textures=(), uniform_buffers=(), storage_buffers=()) -> 'Scope':
          '''
              Create a :py:class:`Scope` object.
  
              Args:
                  framebuffer (Framebuffer): The framebuffer to use when entering.
                  enable_only (int): The enable_only flags to set when entering.
  
              Keyword Args:
                  textures (list): List of (texture, binding) tuples.
                  uniform_buffers (list): List of (buffer, binding) tuples.
                  storage_buffers (list): List of (buffer, binding) tuples.
          '''
  
          textures = tuple((tex.mglo, idx) for tex, idx in textures)
          uniform_buffers = tuple((buf.mglo, idx) for buf, idx in uniform_buffers)
          storage_buffers = tuple((buf.mglo, idx) for buf, idx in storage_buffers)
  
          res = Scope.__new__(Scope)
          res.mglo = self.mglo.scope(framebuffer.mglo, enable_only, textures, uniform_buffers, storage_buffers)
          res.ctx = self
          return res
  
      def simple_framebuffer(self, size, components=4, *, samples=0, dtype='f1') -> 'Framebuffer':
          '''
              A :py:class:`Framebuffer` is a collection of buffers that can be used as the destination for rendering.
              The buffers for Framebuffer objects reference images from either Textures or Renderbuffers.
  
              Args:
                  size (tuple): The width and height of the renderbuffer.
                  components (int): The number of components 1, 2, 3 or 4.
  
              Keyword Args:
                  samples (int): The number of samples. Value 0 means no multisample format.
                  dtype (str): Data type.
  
              Returns:
                  :py:class:`Framebuffer` object
          '''
  
          return self.framebuffer(
              self.renderbuffer(size, components, samples=samples, dtype=dtype),
              self.depth_renderbuffer(size, samples=samples),
          )
  
      def framebuffer(self, color_attachments=(), depth_attachment=None) -> 'Framebuffer':
          '''
              A :py:class:`Framebuffer` is a collection of buffers that can be used as the destination for rendering.
              The buffers for Framebuffer objects reference images from either Textures or Renderbuffers.
  
              Args:
                  color_attachments (list): A list of :py:class:`Texture` or :py:class:`Renderbuffer` objects.
                  depth_attachment (Renderbuffer or Texture): The depth attachment.
  
              Returns:
                  :py:class:`Framebuffer` object
          '''
  
          if type(color_attachments) is Texture or type(color_attachments) is Renderbuffer:
              color_attachments = (color_attachments,)
  
          ca_mglo = tuple(x.mglo for x in color_attachments)
          da_mglo = None if depth_attachment is None else depth_attachment.mglo
  
          res = Framebuffer.__new__(Framebuffer)
          res.mglo, res._size, res._samples, res._glo = self.mglo.framebuffer(ca_mglo, da_mglo)
          res._color_attachments = tuple(color_attachments)
          res._depth_attachment = depth_attachment
          res.ctx = self
          return res
  
      def renderbuffer(self, size, components=4, *, samples=0, dtype='f1') -> 'Renderbuffer':
          '''
              :py:class:`Renderbuffer` objects are OpenGL objects that contain images.
              They are created and used specifically with :py:class:`Framebuffer` objects.
  
              Args:
                  size (tuple): The width and height of the renderbuffer.
                  components (int): The number of components 1, 2, 3 or 4.
  
              Keyword Args:
                  samples (int): The number of samples. Value 0 means no multisample format.
                  dtype (str): Data type.
  
              Returns:
                  :py:class:`Renderbuffer` object
          '''
  
          res = Renderbuffer.__new__(Renderbuffer)
          res.mglo, res._glo = self.mglo.renderbuffer(size, components, samples, dtype)
          res._size = size
          res._components = components
          res._samples = samples
          res._dtype = dtype
          res._depth = False
          res.ctx = self
          return res
  
      def depth_renderbuffer(self, size, *, samples=0) -> 'Renderbuffer':
          '''
              :py:class:`Renderbuffer` objects are OpenGL objects that contain images.
              They are created and used specifically with :py:class:`Framebuffer` objects.
  
              Args:
                  size (tuple): The width and height of the renderbuffer.
  
              Keyword Args:
                  samples (int): The number of samples. Value 0 means no multisample format.
  
              Returns:
                  :py:class:`Renderbuffer` object
          '''
  
          res = Renderbuffer.__new__(Renderbuffer)
          res.mglo, res._glo = self.mglo.depth_renderbuffer(size, samples)
          res._size = size
          res._components = 1
          res._samples = samples
          res._dtype = 'f4'
          res._depth = True
          res.ctx = self
          return res
  
      def compute_shader(self, source) -> 'ComputeShader':
          '''
              A :py:class:`ComputeShader` is a Shader Stage that is used entirely for computing arbitrary information.
              While it can do rendering, it is generally used for tasks not directly related to drawing.
  
              Args:
                  source (str): The source of the compute shader.
  
              Returns:
                  :py:class:`ComputeShader` object
          '''
  
          res = ComputeShader.__new__(ComputeShader)
          res.mglo, ls1, ls2, ls3, ls4, res._glo = self.mglo.compute_shader(source)
  
          members = {}
  
          for item in ls1:
              obj = Uniform.__new__(Uniform)
              obj.mglo, obj._location, obj._array_length, obj._dimension, obj._name = item
              members[obj.name] = obj
  
          for item in ls2:
              obj = UniformBlock.__new__(UniformBlock)
              obj.mglo, obj._index, obj._size, obj._name = item
              members[obj.name] = obj
  
          res._members = members
          res.ctx = self
          return res
  
      def sampler(self, repeat_x=True, repeat_y=True, repeat_z=True, filter=None, anisotropy=1.0,
                  compare_func='?', border_color=None, min_lod=-1000.0, max_lod=1000.0) -> Sampler:
          '''
              Create a :py:class:`Sampler` object.
  
              Keyword Arguments:
                  repeat_x (bool): Repeat texture on x
                  repeat_y (bool): Repeat texture on y
                  repeat_z (bool): Repeat texture on z
                  filter (tuple): The min and max filter
                  anisotropy (float): Number of samples for anisotropic filtering. Any value greater than 1.0 counts as a use of anisotropic filtering
                  compare_func: Compare function for depth textures
                  border_color (tuple): The (r, g, b, a) color for the texture border.
                                        When this value is set the ``repeat_`` values are overriden
                                        setting the texture wrap to return the border color when outside ``[0, 1]`` range.
                  min_lod (float): Minimum level-of-detail parameter (Default ``-1000.0``).
                                   This floating-point value limits the selection of highest resolution mipmap (lowest mipmap level)
                  max_lod (float): Minimum level-of-detail parameter (Default ``1000.0``).
                                   This floating-point value limits the selection of the lowest resolution mipmap (highest mipmap level)
          '''
  
          res = Sampler.__new__(Sampler)
          res.mglo, res._glo = self.mglo.sampler()
          res.ctx = self
          res.repeat_x = repeat_x
          res.repeat_y = repeat_y
          res.repeat_z = repeat_z
          res.filter = filter or (9729, 9729)
          res.anisotropy = anisotropy
          res.compare_func = compare_func
          if border_color:
              res.border_color = border_color
          res.min_lod = min_lod
          res.max_lod = max_lod
          return res
  
      def clear_samplers(self, start=0, end=-1):
          '''
              Unbinds samplers from texture units.
              Sampler bindings do clear automatically between every frame,
              but lingering samplers can still be a source of weird bugs during the frame rendering.
              This methods provides a fairly brute force and efficient way to ensure texture units are clear.
  
              Keyword Arguments:
  
                  start (int): The texture unit index to start the clearing samplers
                  stop (int): The texture unit index to stop clearing samplers
              
              Example::
  
                  # Clear texture unit 0, 1, 2, 3, 4
                  ctx.clear_samplers(start=0, end=5)
  
                  # Clear texture unit 4, 5, 6, 7
                  ctx.clear_samplers(start=4, end=8)
          '''
          self.mglo.clear_samplers(start, end)
  
      def core_profile_check(self) -> None:
          '''
              Core profile check.
  
              FOR DEBUG PURPOSES ONLY
          '''
  
          profile_mask = self.info['GL_CONTEXT_PROFILE_MASK']
          if profile_mask != 1:
              warnings.warn('The window should request a CORE OpenGL profile')
  
          version_code = self.version_code
          if not version_code:
              major, minor = map(int, self.info['GL_VERSION'].split('.', 2)[:2])
              version_code = major * 100 + minor * 10
  
          if version_code < 330:
              warnings.warn('The window should support OpenGL 3.3+ (version_code=%d)' % version_code)
  
      def release(self) -> None:
          '''
              Release the ModernGL object.
          '''
  
          self.mglo.release()
  
  
  def create_context(require=None) -> Context:
      '''
          Create a ModernGL context by loading OpenGL functions from an existing OpenGL context.
          An OpenGL context must exists. If rendering is done without a window please use the
          :py:func:`create_standalone_context` instead.
  
          Example::
  
              # Accept the current context version
              ctx = moderngl.create_context()
  
              # Require at least OpenGL 4.3
              ctx = moderngl.create_context(require=430)
  
          Keyword Arguments:
              require (int): OpenGL version code.
  
          Returns:
              :py:class:`Context` object
      '''
  
      ctx = Context.__new__(Context)
      ctx.mglo, ctx.version_code = mgl.create_context()
      ctx._screen = ctx.detect_framebuffer(0)
      ctx.fbo = ctx.detect_framebuffer()
      ctx.mglo.fbo = ctx.fbo.mglo
      ctx._info = None
  
      if require is not None and ctx.version_code < require:
          raise ValueError('The version required is not provided')
  
      return ctx
  
  
  def create_standalone_context(require=None, **settings) -> 'Context':
      '''
          Create a standalone ModernGL context.
  
          Example::
  
              # Create a context with highest possible supported version
              ctx = moderngl.create_context()
  
              # Require at least OpenGL 4.3
              ctx = moderngl.create_context(require=430)
  
          Keyword Arguments:
              require (int): OpenGL version code.
  
          Returns:
              :py:class:`Context` object
      '''
  
      backend = os.environ.get('MODERNGL_BACKEND')
      if backend is not None:
          settings['backend'] = backend
  
      ctx = Context.__new__(Context)
      ctx.mglo, ctx.version_code = mgl.create_standalone_context(settings)
      ctx._screen = None
      ctx.fbo = None
      ctx._info = None
  
      if require is not None and ctx.version_code < require:
          raise ValueError('Requested OpenGL version {}, got version {}'.format(
              require, ctx.version_code))
  
      return ctx
  