How to set up __init__.py for my addon? I've written a script and am trying to turn it into an addon.  I've been trying to copy an existing addon which is working, but for some reason my variation isn't working.
I've created a directory that has an __init__.py in the root describing the addon and my script file in a subdirectory called ops.  I then zip it into a zipfile.  I then start a new Blender session, open the Preferences menu and install my addon.  Everything seems to go well and I get the message
Modules Installed () from 'D:\\dev\\testDeploy\\modalDrawOperator.zip' into 'C:\\Users\\kitfox\\AppData\\Roaming\\Blender Foundation\\Blender\\2.91\\scripts\\addons'

However, my addon isn't then added to the list of the available addons.  Why is this failing?
__init__.py:
bl_info = {
    &quot;name&quot;: &quot;Normal Tool&quot;,
    &quot;description&quot;: &quot;Run the example modal draw operator.&quot;,
    &quot;author&quot;: &quot;Test&quot;,
    &quot;version&quot;: (1, 0),
    &quot;blender&quot;: (2, 80, 0),
    &quot;location&quot;: &quot;View3D&quot;,
    &quot;category&quot;: &quot;Generic&quot;
}


if &quot;bpy&quot; in locals():
    import importlib
    if &quot;modalDrawOperator&quot; in locals():
        importlib.reload(modalDrawOperator)
else:
    from .ops import modalDrawOperator

import bpy

def register():
    modalDrawOperator.register()


def unregister():
    modalDrawOperator.unregister()

ops/modalDrawOperator.py:
(this is just copied from the examples in the script templates)
import bpy
import bgl
import blf
import gpu
from gpu_extras.batch import batch_for_shader


def draw_callback_px(self, context):
    print(&quot;mouse points&quot;, len(self.mouse_path))

    font_id = 0  # XXX, need to find out how best to get this.

    # draw some text
    blf.position(font_id, 15, 30, 0)
    blf.size(font_id, 20, 72)
    blf.draw(font_id, &quot;Hello Word &quot; + str(len(self.mouse_path)))

    # 50% alpha, 2 pixel width line
    shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
    bgl.glEnable(bgl.GL_BLEND)
    bgl.glLineWidth(2)
    batch = batch_for_shader(shader, 'LINE_STRIP', {&quot;pos&quot;: self.mouse_path})
    shader.bind()
    shader.uniform_float(&quot;color&quot;, (0.0, 0.0, 0.0, 0.5))
    batch.draw(shader)

    # restore opengl defaults
    bgl.glLineWidth(1)
    bgl.glDisable(bgl.GL_BLEND)


class ModalDrawOperator(bpy.types.Operator):
    &quot;&quot;&quot;Draw a line with the mouse&quot;&quot;&quot;
    bl_idname = &quot;view3d.modal_operator&quot;
    bl_label = &quot;Simple Modal View3D Operator&quot;

    def modal(self, context, event):
        context.area.tag_redraw()

        if event.type == 'MOUSEMOVE':
            self.mouse_path.append((event.mouse_region_x, event.mouse_region_y))

        elif event.type == 'LEFTMOUSE':
            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
            return {'FINISHED'}

        elif event.type in {'RIGHTMOUSE', 'ESC'}:
            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
            return {'CANCELLED'}

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        if context.area.type == 'VIEW_3D':
            # the arguments we pass the the callback
            args = (self, context)
            # Add the region OpenGL drawing callback
            # draw in view space with 'POST_VIEW' and 'PRE_VIEW'
            self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')

            self.mouse_path = []

            context.window_manager.modal_handler_add(self)
            return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, &quot;View3D not found, cannot run operator&quot;)
            return {'CANCELLED'}


def register():
    bpy.utils.register_class(ModalDrawOperator)


def unregister():
    bpy.utils.unregister_class(ModalDrawOperator)


if __name__ == &quot;__main__&quot;:
    register()
Looks like my problem was that in the zip archive I made, I was putting my files in the root.  I had to create a subdirectory within my zip with the name of the addon, and then place the source files there.