Skip to content

Instantly share code, notes, and snippets.

@tera3939
Created May 28, 2017 12:21
Show Gist options
  • Save tera3939/cf4d04292c59c2dab2df5b5e156cd940 to your computer and use it in GitHub Desktop.
Save tera3939/cf4d04292c59c2dab2df5b5e156cd940 to your computer and use it in GitHub Desktop.
Pythonのimport文をホックするやつ
import sys
from . import music_importer
sys.meta_path.append(music_importer.MusicImporter)
# このモジュールでimport sysされているため、必ずsys.modulesにsysはある
sys.modules['sys'] = sys
# -*- coding: utf-8 -*-
import time
import wave
from types import ModuleType
from typing import Dict, List, Optional
from pathlib import Path
from importlib.abc import MetaPathFinder, Loader
from importlib.machinery import ModuleSpec
import pyaudio
ModulePaths = List[str]
class AudioModule(ModuleType):
def __init__(self, spec: ModuleSpec, path: str):
self.__name__ = spec.name
self.__loader__ = spec.loader
if spec.submodule_search_locations is not None:
self.__path__ = spec.submodule_search_locations
if spec.parent is not None:
self.__package__ = spec.parent
self._p = pyaudio.PyAudio()
self._wf = wave.open(path, 'rb')
self._stream = self._p.open(
format=self._p.get_format_from_width(self._wf.getsampwidth()),
channels=self._wf.getnchannels(),
rate=self._wf.getframerate(),
output=True,
stream_callback=self._callback
)
def _callback(self, in_data: "型がわからねえ", frame_count: int,
time_info: Dict[str, float], status: int):
data = self._wf.readframes(frame_count)
return (data, pyaudio.paContinue)
def start(self):
self._stream.start_stream()
while self._stream.is_active():
time.sleep(0.1)
def close(self):
self._stream.stop_stream()
self._wf.close()
self._stream.close()
self._p.terminate()
class MusicImporter(Loader, MetaPathFinder):
_root = Path("C:/Users/tera/Music")
@classmethod
def create_module(self, spec: ModuleSpec) -> Optional[ModuleType]:
"""ロード中にモジュールオブジェクトを作成して返す関数
get_code()にspec.nameを渡して実行、返り値をモジュールに組み込み返す
ここではModuleTypeに必要な属性を詰め込むことでモジュールを作成する.
引数:
spec: Finder.find_spec()が返すmodule spec
"""
abs_path = str(self._root / (spec.name + ".wav"))
audio = AudioModule(spec, abs_path)
return audio
@classmethod
def exec_module(self, module: ModuleType):
"""モジュールの名前空間で実行 == exec(code, module.__dict__)
ここでは動画を再生する
"""
print(module.__dict__)
module.start()
module.close()
@staticmethod
def module_repr(module: ModuleType) -> str:
return '<module {!r} (video)>'.format(module.__name__)
@classmethod
def load_module(self, fullname: str):
pass
@classmethod
def find_spec(cls, fullname: str, path: ModulePaths=None,
target: ModuleSpec=None) -> Optional[ModuleSpec]:
""" BuiltinImporterではis_builtinでfullnameがbuiltinか判定し、
そうである場合にその名前を持ちoriginがbuiltinであるmodule specを返している。
ここでは/path/to/videoにfullname+拡張子の名前を持つファイルが無ければNoneを、
あればその名前とLoaderとしてVideoMetaPathLoaderを持つmodule specを返す。
引数:
fullname: インポートされるモジュールの完全修飾名。
path: インポートされるモジュールの__path__属性の値。
最上位のモジュールの場合はNoneとなる。
target: 後でロードされるターゲットとなる既存のモジュール。
importlib.reload()等によってリロードされるときセットされる。
"""
filename = fullname + ".wav"
if (cls._root / filename).is_file():
m = ModuleSpec(fullname, cls)
return m
else:
# dirの場合はエラー(気が向いたら対応)
raise ImportError
@classmethod
def find_module(cls, fullname: str,
path: ModulePaths=None) -> Optional[Loader]:
""" BuiltinImporterではclsのfind_spec()を呼んで返り値のloader属性を返している。
"""
spec = cls.find_spec(fullname, path)
return spec.loader if spec is not None else None
if __name__ == '__main__':
import sys
sys.meta_path.append(MusicImporter)
import music_name
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment