2017-01-25 13 views
3

내가이 같은 모듈 파일이 있다고 가정.가져온 소스 코드를 즉시 수정하는 방법은 무엇입니까? <pre><code># my_module.py print("hello") </code></pre> <p></p>가 그럼 난 간단한 스크립트가 있습니다 :</p> <pre><code># my_script.py import my_module </code></pre> <p>이 <code>"hello"</code>를 인쇄합니다

print() 함수를 "오버라이드"하여 "world"을 대신 반환한다고 가정 해 봅시다. 프로그래밍 방식으로이 작업을 수행하려면 어떻게해야합니까 (수동으로 my_module.py을 수정하지 않고)? 내가 생각 무엇


내가 전이나 그것을 가져 오는 동안 my_module의 소스 코드를 수정 어떻게 든해야한다는 것입니다. Obvisouly, 수입 후 이것을 할 수 없으므로 unittest.mock을 사용하는 솔루션은 불가능합니다.

또한 my_module.py 파일을 읽고 수정 한 다음로드 할 수 있다고 생각했습니다. 하지만 모듈이 다른 곳에 있으면 작동하지 않기 때문에 이것은 추한 것입니다.

좋은 해결책은 내 생각에 importlib을 사용하는 것입니다.

나는 문서를 읽고 아주 교차하는 방법을 발견했다 : get_source(fullname). 난 그냥 그것을 무시할 수 있다고 생각 :

def get_source(fullname): 
    source = super().get_source(fullname) 
    source = source.replace("hello", "world") 
    return source 

불행하게도, 나는 조금 모든 추상 클래스와 함께 손실 내가 제대로이를 수행하는 방법을 모르는입니다.

내가 헛되이 시도 :

spec = importlib.util.find_spec("my_module") 
spec.loader.get_source = mocked_get_source 
module = importlib.util.module_from_spec(spec) 

어떤 도움이 환영하십시오 것입니다.

+0

는'my_module'이 ('인쇄를 정의하지 않습니다)'파이썬 3에 내장 된 기능은 다음과 같습니다 파이썬 2와 호환성이 필요한 경우

, 나는 소스 파일을 읽는 것보다 다른 방법을 볼 수 없습니다 .엑스. – martineau

+0

@martineau 귀하의 요점을 이해하지 못합니다. 파이썬 3을 사용하기 때문에'print()'를 정의하지 않아도 문제없이 사용할 수 있습니다. – Delgan

+0

당신은 당신이'print()'함수를 오버라이드하고 싶다고 말했고, 나는 당신이 가져올 모듈에 정의되어 있지 않다는 것을 지적했다. – martineau

답변

1

다음은 콘텐츠를 this great talk (으)로 해킹 한 것입니다. 지정된 모듈을 가져 오기 전에 임의의 수정을 소스에 적용 할 수 있습니다. 슬라이드가 중요하지 않은 것을 생략하지 않은 한 합리적으로 정확해야합니다. 이것은 파이썬 3.5+에서만 작동합니다.

import importlib 
import sys 

def modify_and_import(module_name, package=None, modification_func): 
    spec = importlib.util.find_spec(module_name, package) 
    source = spec.loader.get_source(module_name) 
    new_source = modification_func(source) 
    module = importlib.util.module_from_spec(spec) 
    codeobj = compile(new_source, module.__spec__.origin, 'exec') 
    exec(codeobj, module.__dict__) 
    sys.modules[module_name] = module 
    return module 

그래서,이 사용하는 당신은

my_module = modify_and_import("my_module", None, lambda src: src.replace("hello", "world")) 
+0

저를 도와 주셔서 감사합니다! 귀하의 솔루션이 아마도 최선의 방법 일 것입니다. – Delgan

-1

하지 우아하지만은 (경로를 추가해야 할 수도 있습니다) 나를 위해 작동 :

with open ('my_module.py') as aFile: 
    exec (aFile.read() .replace (<something>, <something else>)) 
+0

나는 모듈 경로를 지정하지 않아도되고 싶다고 precised. 게다가,'exec()'가 우아하지 않다는 말처럼, 더 나은 해결책이 존재해야합니다. – Delgan

0

는 IT 괜찮 패치 전에 모듈을 수입하고 가능한 해결책이 될 경우

import inspect 

import my_module 

source = inspect.getsource(my_module) 
new_source = source.replace('"hello"', '"world"') 
exec(new_source, my_module.__dict__) 

좀 더 일반적인 해결책을 찾은 경우 잠시 전에 another answer에서 사용한 방법을 살펴볼 수도 있습니다.

+0

이게 나에게 얼마나 유용 할 수 있니? 해결 방법을 사용하여 인쇄 값을 어떻게 바꿀 것입니까? – Delgan

+0

미안하지만, 모듈의 모든 부분을 원숭이 패치하는 일반적인 방법이 필요하다고 가정했습니다. 질문을 다시 읽으면 모듈을 먼저 가져 오는 것을 피하는 것 같습니다.이 경우에는 내 솔루션이 여기 관련이 없습니다. –

+0

나는 나의 대답을 완전히 다시 썼다. 이게 너에게 유용 할까? 그렇지 않으면 삭제하겠습니다. –

0

이 동적으로 가져온 모듈의 소스 코드를 수정의 일반적인 질문에 대답하지 않지만, "무시"또는 "원숭이 패치 수 "(파이썬   3.x에 내장 된 함수이기 때문에) 함수를 사용할 수 있습니다.내가 먼저 더 나은 import 작업을 이해하는 데 필요한

#!/usr/bin/env python3 
# my_script.py 

import builtins 

_print = builtins.print 

def my_print(*args, **kwargs): 
    _print('In my_print: ', end='') 
    return _print(*args, **kwargs) 

builtins.print = my_print 

import my_module # -> In my_print: hello 
0

방법은 다음과 같습니다. 다행히도 이것은 the importlib documentation으로 잘 설명되어 있으며 the source code을 통해 긁어 내면 도움이됩니다.

import 프로세스는 실제로 두 부분으로 나뉩니다. 먼저 finder이 모듈 이름을 파싱하고 (점으로 구분 된 패키지 포함) 적절한 loader을 인스턴스화합니다. 사실, 내장은 예를 들어 로컬 모듈로 가져 오기되지 않습니다. 그런 다음 파인더가 반환 한 내용에 따라 로더가 호출됩니다. 이 로더는 파일 또는 캐시에서 소스를 가져오고 모듈이 이전에로드되지 않은 경우 코드를 실행합니다.

이것은 매우 간단합니다. 왜 실제로는 importutil.abc의 추상 클래스를 사용할 필요가 없는지를 설명합니다. 직접 가져 오기 프로세스를 제공하고 싶지 않습니다. 대신, importuil.machinery의 클래스 중 하나에서 상속 된 서브 클래스를 만들고 get_source()SourceFileLoader에서 재정의 할 수 있습니다. 그러나 이것은 로더가 파인더에 의해 인스턴스화되어 클래스가 사용되는 손이 없기 때문에 갈 방법이 아닙니다. 내 하위 클래스를 사용해야한다고 지정할 수 없습니다.

그래서 가장 좋은 해결책은 파인더가 작업을 수행하게하고 Loader가 인스턴스화 된 것의 get_source() 메소드를 대체하는 것입니다.

아쉽게도 코드 소스를 살펴보면 기본 로더가 모듈에서만 사용되는 get_source()을 사용하지 않는다는 것을 알았습니다. 그래서 내 생각은 효과가 없었습니다.

마지막으로 get_source()을 수동으로 호출해야하며 반환 된 소스를 수정해야하며 마지막으로 코드를 실행해야합니다. 이것은 Martin Valgur가 his answer에 상술 한 것입니다.

import imp 
import sys 
import types 

module_name = "my_module" 

file, pathname, description = imp.find_module(module_name) 

with open(pathname) as f: 
    source = f.read() 

source = source.replace('hello', 'world') 

module = types.ModuleType(module_name) 
exec(source, module.__dict__) 

sys.modules[module_name] = module