OpenGL 2 compatibility changes

Modifications fall into a few categories: 1) OpenGL 2 compatibility changes. 2) convenience; these just reduce the amount of boilerplate code needed.  3) Recommended API changes, some of which may break some existing code. The changes reflect the way it seems most intuitive for me to write my code, so please do not hesitate to reject any of them (especially 2 and 3); some of them are a bit contrary to the style of the library so far.

* Several of the enums listed in shader.py do not exist in earlier versions of OpenGL. To work around this, I added generate_enum_map, which checks for these at run time.

* I made a few changes to automate some of the process of attaching data to shader uniforms and attributes, such that the final code looks cleaner. It also obscures some of the inner workings, which I can understand may not be desirable. In particular, I added \__getitem__ and \__setitem__ methods to ShaderProgram, as well as Attribute.set_buffer which is needed for OpenGL 2. 

* Fixed up Buffer.set_data, which appeared to be broken. Also some minor changes that make Buffer easier to use when you just have a single, contiguous array to upload.

* In my Buffer.set_data, I removed the assertion and just bind / unbind the buffer to set the data. I noticed you generally avoid this, preferring to let the user take care of binding. Is there a reason for this? I think binding to global variables is one of the most awkward aspects of OpenGL and would prefer to automate it as much as possible. 

Below is an example of how I'm using these changes (sans shaders). The major differences are that each VBO is built in a single line, and uniforms / attributes are accessed directly from the shader and automatically configured (Buffer carries a little more information about its data--ndim, shape, and dtype--to make this possible). 

    class PlotLine:
        def __init__(self, pos, color, width, connect):
            self.pos = pos
            self.color = color
            self.width = width
            self.connect = connect
            
        def init_gl(self):
            ## shaders define transform uniforms
            ## and in_ attributes
            self.program = pygly.shader.ShaderProgram(
                pygly.shader.VertexShader(vertex_shader),
                pygly.shader.FragmentShader(fragment_shader),
                )
                
            self.update_buffers()
            
        def update_buffers(self):
            self.width_max = self.width.max()
            self.vbo = pygly.buffer.Buffer(data=self.pos)
            self.cbo = pygly.buffer.Buffer(data=self.color)
            self.lbo = pygly.buffer.Buffer(data=self.width)
            self.conbo = pygly.buffer.Buffer(data=self.connect)
            
        def draw(self, transform, view_transform):
            self.program.bind()
            
            self.program['transform'] = transform
            ## maps final clipping rect to viewport coordinates (needed for antialiasing)
            self.program['view_transform'] = view_transform

            self.program['in_position'] = self.vbo
            self.program['in_color'] = self.cbo
            self.program['in_width'] = self.lbo
            self.program['in_connect'] = self.conbo
            
            glEnable(GL_BLEND)
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
            glLineWidth(self.width_max+1)
            glDrawArrays(GL_LINE_STRIP, 0, self.pos.shape[0])
            glBindBuffer(GL_ARRAY_BUFFER, 0)
            glUseProgram(0)