soma0sd

코딩 & 과학 & 교육

Python 패키지 개발일지 09: 패키지를 갈아엎게 하는 문제들

반응형

패키지를 제작하다 보면 미묘하게 신경쓰이는데 이미 만들어놓은 구조 때문에 고치기가 난감한 문제들이 있습니다. 이대로 업데이트가 쌓이다 보면 조금 신경쓰이는 문제가 계속 남아 점점 기술부채가 증가하게 됩니다. 여기서는 패키지를 갈아엎게 만들었던 문제와 그 해결법을 다룹니다.

버전 규칙

기존 다른 패키지를 작업할 때는 버전 개념을 잘 몰라 업데이트 할 때마다 계속 숫자를 올리기만 했는데요. 이 때문에 업데이트의 규모와 성격을 알 수 없어서 관리하기가 복잡했던 경험이 있습니다.

현재 만들고 있는 AutoWinPy는 파이썬의 버전규칙과 동일한 세 구역으로 구분한 버전 규칙을 따르고 있습니다. 이 방식은 v1.6.32와 같은 형태를 가집니다.

  • 세 번째 자리(예시의 32): 버그 수정 업데이트를 의미합니다.
  • 두 번째 자리(예시의 6): 새로운 함수, 메소드, 키워드를 추가하는 경우입니다. 단 동일 계열의 호환성을 보장해야 합니다. 예시의 경우 v1.6업데이트는 패키지 버전 v1.5, v1.4, v1.3에서 작동하던 소스코드가 문제없이 작동해야 하며 동일한 결과를 보여야 합니다.
  • 첫 번째 자리(예시의 1): 패키지의 파일 시스템 변경, 네임스페이스 변경, 기존 기능의 제거나 변경, 종속성 변경 등 이전 버전의 패키지와의 호환성을 보장할 수 없는 경우 변경합니다. 파이썬 2와 파이썬 3의 차이를 생각하시면 됩니다.

마침표(.)로 구분한 숫자는 자릿수가 아니므로 각 자리의 숫자가 두자릿수여도 상관없습니다.

예외가 있습니다. 첫 번째 자리가 0인 경우에는 정식 출시 버전인 >1을 넘지 않는 비정식 버전임을 나타내므로 버그 수정을 제외한 대부분의 변경이 두 번째 자릿수의 변경이 됩니다. v0.3v0.1은 하위 호환을 반드시 보장하지 않습니다.

패키지 이름공간 트릭

현재 작업중인 AutoWinPy의 0.3.2에서 0.4.0 사이에는 대대적인 구조 변화가 있었습니다. 이름공간(namespace)을 제대로 활용하지 못해서 모듈이 사용하는 기본 패키지와 모듈이 패키지의 내부모듈처럼 다뤄지는 문제를 해결하기 위해 이름공간 트릭을 써야 했거든요.

모듈 이름공간 트릭

win32.py

import os
import cv2

def function(arg):
  return out

패키지에서 이런 모듈을 가져오려고 한다면 autowinpy.win32과 같은 방식으로 불러올 수 있습니다. 그러나, 원래 의도가 function()만 사용하는 것이 목적이었음에도 autowinpy.win32.os, autowinpy.win32.cv2등 모듈 내에서 불러온 패키지를 문제 없이 사용할 수 있으며 지능형 코드 완성(IntelliSense)은 이들을 패키지에서 제공하는 요소로 판단하고 패키지 요소로 등록합니다. 결과물에는 문제가 없지만 사용자들에게 패키지와 모듈의 기능에 대한 혼란을 줄 수 있기 때문에 사용자에게 불필요한 패키지나 모듈의 이름은 감추는 것이 좋을 겁니다.

my_package/win32/_function.py

import os
import cv2

def function(arg):
  return out

my_package/win32/__init__.py

from ._function import function

my_package/__init__.py

from . import win32

이런 방식으로 내부에 패키지를 만들어서 필요한 함수나 클래스만 불러오면 사용자에게 의도한 함수만 보여줄 수 있습니다. 그러나 파이썬의 명시성은 절대적이기 때문에 my_package.win32._function.os와 같은 방식은 여전히 통합니다.

패키지 이름공간 트릭

패키지 이름에 바로 붙일 클래스나 함수의 경우에도 마찬가지로 내부 패키지를 만들어서 해결합니다. 이 경우 __all__을 활용하여 이름공간을 변경할 수 있습니다.

my_package/core/_function.py

import os
import cv2

def function(arg):
  return out

my_package/core/__init__.py

from ._function import function

__all__ = [
    "function",
]

my_package/__init__.py

from .core import *

내부패키지 core에 있는 함수를 my_package.function()과 같은 방식으로 사용하기 위한 이름공간 트릭입니다.

반응형
태그:

댓글

End of content

No more pages to load