django 개발 자 모드 의 autoreload 가 어떻게 실현 되 었 는 지 에 대해 말씀 드 리 겠 습 니 다.

8680 단어 djangoautoreload
django 응용 프로그램 을 개발 하 는 과정 에서 개발 자 모드 로 서 비 스 를 시작 하 는 것 은 매우 편리 한 일이 다.python manage.py runserver 만 있 으 면 서 비 스 를 실행 할 수 있 고 매우 인성 화 된 autoreload 체 제 를 제공 하 며 수 동 으로 프로그램 을 다시 시작 하지 않 아 도 코드 를 수정 하고 피드백 을 볼 수 있다.처음 접 촉 했 을 때 이 기능 이 비교적 인성 화 되 었 다 고 생각 했 고 특별히 높 은 기술 이 라 고 생각 하지 않 았 다.나중에 시간 이 나 면 제 가 이 autoreload 를 실현 하면 어떻게 할 까 생각 했 습 니 다.오랫동안 생각 하지 못 했 는데 잘 모 르 는 부분 이 있 었 습 니 다.첫 번 째 반응 은 정말 눈 이 높 고 손 이 낮은 것 같 습 니 다.그래서 django 가 어떻게 autoreload 를 실현 하 는 지 연구 하 는 데 시간 이 걸 렸 습 니 다.한 걸음 한 걸음 소스 코드 를 보고 말 을 하 는 것 은 당연 하 다 고 생각 하지 않 습 니 다.
1.runserver 명령.본론 으로 들 어가 기 전에 runserver 명령 을 어떻게 실행 하 는 지 에 관 한 쓸데없는 말 이 있 습 니 다.주제 와 관계 가 크 지 않 으 므 로 간단하게 가 져 가 겠 습 니 다.
명령 행 이 python manage.py runserver 를 입력 하면 django 는 runserver 라 는 명령 의 실행 모듈 을 찾 아 마지막 으로 떨 어 집 니 다.
django\\contrib\staticfiles\\management\\commands\\runserver.py 모듈 에서:

#django\contrib\staticfiles\management\commands\runserver.py
from django.core.management.commands.runserver import \
Command as RunserverCommand

class Command(RunserverCommand):
help = "Starts a lightweight Web server for development and also serves static files."

이 command 의 실행 함 수 는 다음 과 같 습 니 다:

#django\core\management\commands\runserver.py
class Command(BaseCommand):
  def run(self, **options):
  """
  Runs the server, using the autoreloader if needed
  """
  use_reloader = options['use_reloader']

  if use_reloader:
    autoreload.main(self.inner_run, None, options)
  else:
    self.inner_run(None, **options)
여기 usereloader 의 판단.만약 우리 가 시작 명령 에-noreload 를 추가 하지 않 았 다 면,프로그램 은 autoreload.main 이라는 함 수 를 가 고,추가 하면 self.inner 를 갈 것 입 니 다.run,응용 프로그램 을 직접 시작 합 니 다.
사실 autoreload.main 의 매개 변수 에서 도 알 수 있 듯 이 self.innerrun 은 패 키 지 를 만 들 었 습 니 다.autoreload 의 체 제 는 바로 이 패 키 지 를 통 해 다음 과 같 습 니 다.
PS:원본 코드 를 보 았 을 때 django 의 command 모드 가 여전히 아름 답 게 실현 되 었 음 을 발 견 했 습 니 다.배 울 만 한 가치 가 있 습 니 다.
2.autoreload 모듈.autoreload.main()보기:

#django\utils\autoreload.py:
def main(main_func, args=None, kwargs=None):
  if args is None:
    args = ()
  if kwargs is None:
    kwargs = {}
  if sys.platform.startswith('java'):
    reloader = jython_reloader
  else:
    reloader = python_reloader

  wrapped_main_func = check_errors(main_func)
  reloader(wrapped_main_func, args, kwargs)
jpython 과 다른 python 에 대해 구별 처 리 를 했 습 니 다.먼저 jpython 을 무시 합 니 다.check_errors 는 mainfunc 오류 처리 도 무시 합 니 다.python 보기reloader:

#django\utils\autoreload.py:
def python_reloader(main_func, args, kwargs):
  if os.environ.get("RUN_MAIN") == "true":
    thread.start_new_thread(main_func, args, kwargs)
    try:
      reloader_thread()
    except KeyboardInterrupt:
      pass
  else:
    try:
      exit_code = restart_with_reloader()
      if exit_code < 0:
        os.kill(os.getpid(), -exit_code)
      else:
        sys.exit(exit_code)
    except KeyboardInterrupt:
      pass
처음 여기까지 왔 을 때 환경 변수 중 RUNMAIN 변 수 는"true"가 아니 라 심지어 없습니다.그 러 니까 else 로 가세 요.restartwith_reloader:

#django\utils\autoreload.py:
def restart_with_reloader():
 while True:
   args = [sys.executable] + ['-W%s' % o for o in sys.warnoptions] + sys.argv
    if sys.platform == "win32":
      args = ['"%s"' % arg for arg in args]
    new_environ = os.environ.copy()
    new_environ["RUN_MAIN"] = 'true'
    exit_code = os.spawnve(os.P_WAIT, sys.executable, args, new_environ)
    if exit_code != 3:
      return exit_code
여기 서 먼저 while 순환 을 하고 내 부 는 RUNMAIN 은"true"로 바 뀌 었 습 니 다.그리고 os.spawnve 방법 으로 키 프로 세 스(subprocess)를 엽 니 다.os.spawnve 의 설명 을 보 세 요.

#os.py
def spawnve(mode, file, args, env):
  """spawnve(mode, file, args, env) -> integer

  Execute file with arguments from args in a subprocess with the
  specified environment.
  If mode == P_NOWAIT return the pid of the process.
  If mode == P_WAIT return the process's exit code if it exits normally;
  otherwise return -SIG, where SIG is the signal that killed it. """

  return _spawnvef(mode, file, args, env, execve)
사실은 명령 행 을 다시 한 번 바 꾸 고 python manage.py runserver 를 다시 한 번 걸 었 습 니 다.
이어서 restartwith_reloader 의 while 순환,주의해 야 할 것 은 while 순환 종료 의 유일한 조건 은 exit 입 니 다.code!=3。 하위 프로 세 스 가 종료 되 지 않 으 면 os.spawnve 에서 계속 멈 춥 니 다.하위 프로 세 스 가 종료 되 고 종료 코드 가 3 이 아니라면 while 는 종 료 됩 니 다.3 이면 계속 순환 하고 하위 프로 세 스 를 다시 만 듭 니 다.이 논리 에서 autoreload 의 메커니즘 을 추측 할 수 있 습 니 다.현재 프로 세 스(주 프로 세 스)는 아무것도 하지 않 고 하위 프로 세 스 의 운행 상황 을 감시 합 니 다.하위 프로 세 스 가 야 말로 진정한 일 입 니 다.하위 프로 세 스 가 exit 로code=3 종료(파일 수정 이 감지 되 었 기 때문에)하위 프로 세 스 를 다시 시작 하면 새 코드 가 자 연 스 럽 게 유효 합 니 다.하위 프로 세 스 가 exit 로code!=3.종료 하면 메 인 프로 세 스 도 끝 납 니 다.전체 django 프로그램 은 무릎 을 꿇 어도 됩 니 다.이것 은 단지 추측 일 뿐 이 니,다음은 이어서 검증 하 자.
3.하위 프로 세 스.위 에 사실 한 가지 의문 이 있 습 니 다.다시 시작 한 이상 왜 하위 프로 세 스 가 하위 프로 세 스 를 계속 만 들 지 않 습 니까?그 이 유 는 RUNMAIN 이 환경 변 수 는 메 인 프로 세 스에 서 true 로 바 뀌 었 습 니 다.하위 프로 세 스 는 python 으로 갑 니 다.reloader 함수 일 때:

#django\utils\autoreload.py:
def python_reloader(main_func, args, kwargs):
  if os.environ.get("RUN_MAIN") == "true":
    thread.start_new_thread(main_func, args, kwargs)
    try:
      reloader_thread()
    except KeyboardInterrupt:
      pass
  else:
    try:
      exit_code = restart_with_reloader()
      if exit_code < 0:
        os.kill(os.getpid(), -exit_code)
      else:
        sys.exit(exit_code)
    except KeyboardInterrupt:
      pass
if 조건 이 만족 하면 메 인 프로 세 스 와 다른 논리 적 가 지 를 가 집 니 다.여기 서 먼저 스 레 드 를 열 고 main 을 실행 합 니 다.func,바로 위의 command.innerrun。여기 thread 모듈 은 이렇게 import 입 니 다:

#django\utils\autoreload.py:
from django.utils.six.moves import _thread as thread

여기 six 모듈 의 역할 은 각종 python 버 전 을 호 환 하 는 것 입 니 다.

[codeblock six]
#django\utils\six.py
class _SixMetaPathImporter(object):

"""
A meta path importer to import six.moves and its submodules.

This class implements a PEP302 finder and loader. It should be compatible
with Python 2.5 and all existing versions of Python3
"""

    :
# https://pythonhosted.org/six/
Six: Python 2 and 3 Compatibility Library
Six provides simple utilities for wrapping over differences between Python 2 and Python 3. It is intended to support codebases that work on both Python 2 and 3 without modification. six consists of only one Python file, so it is painless to copy into a project.
그래서 프로그램 이 python 2 와 python 3 에서 모두 뛸 수 있 고 노 방,six 는 중요 한 도구 입 니 다.그리고 시간 을 내 서 six,mark 를 보 세 요.
그리고 다시 reloaderthread:

[codeblock autoreload_reloader_thread]
#django\utils\autoreload.py:
def reloader_thread():
  ensure_echo_on()
  if USE_INOTIFY:
    fn = inotify_code_changed
  else:
    fn = code_changed

  while RUN_RELOADER:
    change = fn()
    if change == FILE_MODIFIED:
      sys.exit(3) # force reload
    elif change == I18N_MODIFIED:
      reset_translations()
    time.sleep(1)
ensure_echo_on()사실 아직 잘 모 르 겠 습 니 다.유 닉 스 시스템 파일 을 대상 으로 처리 한 것 같 습 니 다.먼저 생략 한 것 같 습 니 다.
USE_INOTIFY 도 시스템 파일 작업 과 관련 된 변수 로 inotify 가 파일 변 화 를 감지 하 는 방법 을 선택 할 수 있 는 지 여부 에 따라 선택 할 수 있 습 니 다.
while 순환,1 초 간격 으로 파일 상 태 를 검사 합 니 다.일반 파일 에 변화 가 있 으 면 프로 세 스 가 종료 되 고 종료 코드 는 3 입 니 다.메 인 프로 세 스 를 보 니 종료 코드 가 3 이면 하위 프로 세 스 를 다시 시작 합 니 다.이렇게 하면 위 와 연결된다.일반 파일 변화 가 아니 라 I18NMODIFIED(.mo 접미사 의 파일 변화,바 이 너 리 라 이브 러 리 파일 등),resettranslations,불 러 온 라 이브 러 리 캐 시 를 지우 고 다음 에 다시 불 러 오 라 는 뜻 입 니 다.
이상 은 autoreload 메커니즘 의 절차 입 니 다.그 중에서 일부 세부 사항 은 잘 모 르 겠 습 니 다.예 를 들 어 서로 다른 운영 체제 파일 변화 에 대한 검 측 이지 만 모두 세부 적 인 것 이 고 주류 과정 과 관련 되 지 않 습 니 다.이것 을 보고 나 는 또 나 에 게 autoreload 체 제 를 설계 하 라 고 하면 어떻게 할 것 이 냐 고 물 었 다.지금 내 대답 은 django\utils\autoreload.py 파일 을 직접 가 져 와 서 사용 하 는 거 야.사실 이것 은 매우 독립 된 모듈 이 고 특히 통용 되 며 통용 되 는 autoreload 솔 루 션 으로 충분히 사용 할 수 있 습 니 다.저 는 스스로 털 도 쓸 수 있 습 니 다.
django 개발 자 모드 의 autoreload 가 어떻게 실현 되 었 는 지 에 대한 간단 한 이 야 기 는 바로 편집장 이 여러분 에 게 공유 한 모든 내용 입 니 다.여러분 께 참고 가 되 고 저 희 를 많이 사랑 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기