파이썬을 위한 FreeBSD 플랫폼 라이브러리/커널 인터페이스 바인딩인 py-freebsd을 그동안 버려두고 있었다가, 최근에 쓰겠다는 사람이 나타나서 좀 버전업을 해 볼까 해 보고 있습니다. 흐흐 (그동안은 쓰는 사람이 없어서 –;) 몇 년전까지만 해도 별로 신경을 안 썼던 퇴행 검사(regression test) 코드들을 요새는 뭔가 새로 추가하거나 변경만 하면 넣는 분위기가 되어서, 몇몇 개발자들이 인터페이스 테스트 코드를 작성하기는 해야하는데, C로 일일이 스크립팅하려니 귀찮기가 짝이 없어서 파이썬으로 작성할까 생각해보고 있다고 합니다. 🙂
py-freebsd는 원래 완전 쌩노가다 코드로 직접 다 작성되어 있었는데, 제공하는 함수가 30개를 넘고, 상수 200개 정도에 이제 업그레이드하면서 cd(4)나 cam(4)같은 것들을 C 확장 타입으로 만들려고 하니까 이런 중복 작업으로 점철된 노가다 작업에 회의감이 느껴져서 친도가 안 나갔었습니다. 그 뒤로 벌써 거의 3년이 지났는데, 예전에 사람들이 좋다고 칭찬하던 pyrex가 생각나서, “그래 한번 도전해 보는거야!”
우선, py-freebsd에서 가장 간단한 함수인 getosreldate(3)를 해 봤는데. 오우. 단 몇 줄에 되는군요! (다른 데서 봤지만 직접 해보니 새로운 감동이;;;;)
|
cdef extern int c_getosreldate "getosreldate" () def getosreldate(): cdef int reldate reldate = c_getosreldate() if reldate == -1: raise OSErrorFromErrno() return reldate |
흐흐 물론 SWIG에 비하면 복잡한 방법이기는 하지만, 별도의 파이썬측 모듈을 만들지 않고도 섬세한 리턴값 제어나 입력값 가공이 가능하다는 점은 아주 감동적이라, 아직까지 pyrex로 안 바꾼 것이 참 후회가 되는군요 흐흐.. 그래서 py-freebsd에서 제공하고 있는 대부분의 함수들을 순조롭게 pyrex용으로 바꾸어서 거의 원래 노력형(;;) C 소스에 비해 20%정도의 양으로 줄어버렸습니다~ 🙂
pyrex가 실제 활용하고 있는 사람들에게서 피드백을 많이 받아서 진짜로 필요한 것에 대한 대처가 많은지 의외로 뭔가 하려고 딱 마음을 먹었을 때 막히는 게 없었는데, 꼽아보자면 이런 것들이 있었습니다.
- C스타일 include가 제공된다: 사실 별 것은 아니지만.. 아무래도 FreeBSD C 라이브러리는 네임스페이스가 그냥 평면 구조이기 때문에, 모듈 안에 여러 단계를 나누기가 애매해서, 하나의 C 모듈 안에 모두 들어가는 것이 좋았는데, C 스타일 include를 제공해 줘서, 소스 길이를 적당히 유지하면서도 대형 모듈을 만들 수 있었습니다.
- 트릭이 매뉴얼에 언급이 돼 있다;; 예를 들면 플랫폼에 따라서 크기가 일정하지 않은 자료형으로 되어있는 정수형 매크로를 다루기 위해서, cdef extern int를 from header 블럭 안에 써버리면, 사실 타입은 신경 안 쓰기 때문에, 별 문제 없이 넘어간다는 팁 같은 것이 아예 매뉴얼에 써 있어서, pyrex에서 제공해 주는 것만으로는 해결하기가 쉽지 않은 상황들을 어렵지 않게 편법을 이용해서 빠져나갈 수 있었습니다;; py-freebsd에서도 함수로 cdef extern해놓고서는 중간에 C 헤더 파일을 하나 include 해버려서 거기서 매크로로 선언하는 트릭을 하나.. 🙂
- 사소한 난감함에 대한 배려: FreeBSD의 statfs시스템콜은 struct statfs를 사용하기도 하고, 구현해야하는 파이썬측 함수 이름도 statfs이다보니, 삼중으로 네임 스페이스가 충돌하는데, 이런 경우에 대한 대처가 되어 있었고, 음청나게 많은 enum같은 다른 부분에서도 뭔가 난감하다 싶으면 어렵지 않게 문서에서 해결책을 찾을 수가 있었습니다.
- 널널하고 친숙한 문법: C 문법과 파이썬 문법을 절묘하게 섞어놔서, 매뉴얼을 안 보고도 대충 이렇게 쓰면 되겠지 하고 쓰면 다 되는게 참 신기할 정도로 잘 만들어 뒀군요. 🙂
한편, py-freebsd의 고유 특징인 것 같기도 하지만, 다른 곳에서도 쓰일 법한 것들인데 고려가 되지 않아서 불편한 것도 제법 있었습니다.
- 그때 그때 다른 struct 멤버: OS 버전이 올라가면서 빠진 멤버나 버전이 올라가면서 생긴 멤버 같은 것들을 처리해 주기 위해서 C에서의 전처리자로 뽑아주는 것이 뭔가 필요한데, 이런 것을 하기 위한 방법이 딱히 없어서, setup.py에서 struct 정의를 한번 훑어서 그 결과를 pyrex에서 include하는 방식으로 일단은 해결했습니다. –;
- 엄청나게 많은 상수를 간단하게 정의하기: 커널 API 함수들이다 보니, 특성상 정수형 상수가 엄청나게 많은데, 이런 것도 또 OS 버전에 따라 있기도 하고 없기도 하고, 게다가 그냥 정의하기에는 상수 1개를 정의하기 위해서 3줄씩 써줘야 하기 때문에, 상수 목록을 만들기도 힘들고 관리하기도 힘든 단점이 있어서, 이것도 결국은 간단하게 함수 호출하는 형식으로 pyrex 소스에 적어두면 setup.py에서 컴파일 전에 소스를 스캔해서, 실제 헤더 파일에 있는지 확인해서 있는 것만 cdef extern을 별도의 파일에 넣어서 include하는 방법으로 해결했습니다.. 아주 찝찝하네요 흐~;;
- 워닝이 너무 많아~: 어차피 generate한 소스이니까 워닝이 좀 나더라도 크게 문제가 되는 것은 없지만.. 그래도 워닝이 너무 많이 나서 참 컴파일하고 있기가 불안하다는 문제는 존재합니다. –;;;;;
몇가지 사소한 난점은 있긴 했지만, 그래도 전반적으로 바꾸고 보니 유지보수도 쉬울 것 같고, 앞으로 새로운 기능을 추가하는 것도 재미있을 것 같아서 굉장히 보람이 있습니다. 🙂 pyrex 좋아!