import imp
import inspect
import json
from importlib.machinery import ModuleSpec
from inspect import Parameter, Signature
import os
import sys

import types

class JSONImporter:
    _path = sys.path

    @classmethod
    def find_spec(self, fullname, path=None, target=None):
        name = fullname.rpartition('.')[-1]
        if path is None:
            path = self._path
        for dn in path:
            filename = os.path.join(dn, name + ".json")
            if os.path.exists(filename):
                m = ModuleSpec(name=fullname, loader=JSONLoader(filename), origin=filename)
                return m
        return None

def MethodCopier(from_cls):
    class MethodCopierBase(type):
        def _create_proxy_fn(method):
            return lambda s,*a,**k: method(s._dict, *a, **k)

        def __new__(cls, name, bases, dct):
            for method_name, method in inspect.getmembers(from_cls, predicate=inspect.ismethoddescriptor):
                if method_name in cls.without_methods:
                    continue

                dct[method_name] = cls._create_proxy_fn(method)

            return super().__new__(cls, name, bases, dct)

    return MethodCopierBase

class DictMethodCopier(MethodCopier(dict)):
    without_methods = (
            '__getattribute__',
            '__init__',
            '__doc__',
            '__hash__',
            '__new__',
            '__setattr__',
            '__reduce__',
            '__reduce_ex__',
            '__delattr__',
            '__dir__'
            )


class DictModule(types.ModuleType, metaclass=DictMethodCopier):
    def __init__(self, name, *args, **kwargs):
        self._dict = {}
        super().__init__(name)


class JSONLoader:
    def __init__(self, filename):
        self._filename = filename

    def create_module(self, spec):
        mod = sys.modules.setdefault(spec.name, DictModule(spec.name))
        mod.__file__ = self._filename
        mod.__loader__ = self
        return mod

    def exec_module(self, mod):
        mod.update(json.load(open(self._filename, 'r')))
        return mod


def install_importer():
    sys.meta_path.append(JSONImporter)

install_importer()
