Skip to content

Commit

Permalink
deploy: 5f41033
Browse files Browse the repository at this point in the history
  • Loading branch information
4roring committed Jul 1, 2023
1 parent 73914ac commit 7998b28
Show file tree
Hide file tree
Showing 14 changed files with 345 additions and 12 deletions.
Binary file added _images/00_vscode_setting.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added _images/01_eval1.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added _images/01_vscode_setting.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added _images/02_eval2.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added _images/03_eval3.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added _images/04_eval4.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added _images/05_eval5.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
140 changes: 140 additions & 0 deletions _sources/docs/14_0_c_extension.md
Original file line number Diff line number Diff line change
@@ -1 +1,141 @@
# Python C 확장 개발 가이드

## 참고 페이지
[Real Python Building a Python C Extension Module](https://realpython.com/build-python-c-extension-module/)
[MSDN Python용 C++ 확장 만들기](https://learn.microsoft.com/ko-kr/visualstudio/python/working-with-c-cpp-python-in-visual-studio?view=vs-2022)


## 샘플 코드
저장소 book/c_extension_sample 폴더


## Python C 확장 따라 만들기
원하는 경로, 원하는 IDE에서 작업을 시작할 수 있습니다
VSCode 기준으로 작업을 진행합니다
C/C++ 확장이 설치되어 있어야 편합니다

### VSCode 세팅하기
![VSCode 세팅](../images/14_c_extension/00_vscode_setting.png)
Ctrl + Shift + P 를 눌러 C/C++ Configurations 창을 열어줍니다

![VSCode 세팅](../images/14_c_extension/01_vscode_setting.png)
Include path 에 Python이 설치된 폴더를 보면 include 폴더가 있습니다
해당 경로를 추가해줍니다

### c 확장 모듈 기능 작성하기
이제 간단하게 C API 인 fputs를 python에서 쓸 수 있는 모듈로 개발합니다
```c
#include <Python.h>

static PyObject* method_fputs(PyObject* self, PyObject* args) {
char *str, *filename = NULL;
int bytes_copied = -1;

if(!PyArg_ParseTuple(args, "ss", &str, &filename)) {
return NULL;
}

FILE *fp = fopen(filename, "w");
bytes_copied = fputs(str, fp);
fclose(fp);

return PyLong_FromLong(bytes_copied);
}
```
PyObject* 를 반환하는 fputs static 함수를 작성하였습니다
PyObject 타입은 Python 의 객체 타입으로 **11장 객체와 타입**에서도 설명을 하고 있습니다
```c
...
char *str, *filename = NULL;
int bytes_copied = -1;
if(!PyArg_ParseTuple(args, "ss", &str, &filename)) {
return NULL;
}
...
```
PyArg_ParseTuple 함수는 Python 에서 받은 인수 args를 로컬 변수로 대입합니다
2번째 인자 "ss"는 어떤 타입으로 인수를 파싱할지 설정합니다 (예제에서는 문자열, 문자열로 파싱)
[[c-api arg 관련 공식 문서](https://docs.python.org/3/c-api/arg.html)]
3번째 인자부터는 가변길이 인자로 인수를 대입받을 변수들의 주소값을 차례대로 넘겨줍니다

```c
FILE *fp = fopen(filename, "w");
bytes_copied = fputs(str, fp);
fclose(fp);

return PyLong_FromLong(bytes_copied);
```
받은 인수값으로 파일을 열어 fputs를 수행합니다
그리고 반환값으로 PyLong_FromLong 함수를 통하여 fputs로 쓴 파일 크기를 PyObject로 반환합니다
(PyLong_FromLong에 대한 부분도 11장 객체와 타입 참조)
### c 확장 모듈 초기화 함수 작성하기
위 코드에 이어서 확장 모듈 초기화 부분을 작성합니다
```c
static PyMethodDef FPutsMethods[] = {
{"fputs", method_fputs, METH_VARARGS, "Python interface for fputs C library function"},
{NULL, NULL, 0, NULL}
};
```
모듈에서 정의된 메서드를 인터프리터에 알려주기위한 PyMethodDef 설정
**"fputs"** - Python에서 호출되는 이름
**method_fputs** - 호출되는 C 함수
**METH_VARARGS** - self와 args를 인자로 받는다는 플래그
([c-api sstructures](https://docs.python.org/3/c-api/structures.html))
**마지막 문자열** - 메서드에 대한 docstring

```c
static struct PyModuleDef fputsmodule = {
PyModuleDef_HEAD_INIT,
"fputs",
"Python interface for the fputs C library function",
-1,
FPutsMethods
};
```
PyModuleDef 로 C 확장 모듈 자체에 대한 정보를 저장 ([PyModuleDef 문서](https://docs.python.org/3/c-api/module.html#c.PyModuleDef))
총 9개의 멤버가 있지만 여기서는 필요한 5개만 초기화합니다

**PyModuleDef_HEAD_INIT** - PyModuleDef_Base 유형의 멤버, 하나만 갖도록 권장
**"fputs"** - Python C 확장 모듈 이름 (import fputs)
**3번째 문자열** - 모듈에 대한 docstring
**-1** - 프로그램 상태 저장에 필요한 메모리 크기, 모듈이 여러 서브 인터프리터에서 사용될 때 유용, 음수 값은 서브 인터프리터를 지원하지 않음을 나타냄
**FPutsMethods** - 메서드 테이블에 대한 참조

```c
PyMODINIT_FUNC PyInit_fputs(void) {
return PyModule_Create(&fputsmodule);
}
```
Python에서 모듈을 처음 임포트 할 때 PyInit_fputs를 호출하게 합니다
PyMODINIT_FUNC은 암시적으로 세 가지 작업을 수행
1. 함수 반환형을 PyObject*로 암시적 설정
2. 특별한 연결을 선언
3. 함수를 External "C" 로 선언 (C++의 경우 C++ 컴파일러에 심볼에 대한 이름이 섞이지 않도록 지시)
PyModule_Create는 PyObject* 형의 새로운 모듈 객체를 반환
인자로는 이전에 선언해둔 fputsmodule 객체의 주소를 전달
### setup.py 작성
```python
from distutils.core import setup, Extension
def main():
setup(name="fputs",
version="1.0.0",
description="Python interface for the fputs C library function",
author="<개발자 이름>",
author_email="[email protected]",
ext_modules=[Extension("fputs", ["fputsmodule.c"])])
if __name__ == "__main__":
main()
```
Python 패키지를 distutils를 사용하여 모듈을 빌드 할수 있습니다
내부에서 clang을 사용하여 빌드합니다

```shell
python setup.py install
```
setup.py 작성 후 위 명령어로 실행하면 내가 만든 모듈이 빌드되고 설치됩니다
6 changes: 6 additions & 0 deletions _sources/docs/7_0_compiler.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,9 @@ PyAST_CompileObject()에 컴파일러 상태와 symtable, AST로 파싱된 모
1. 컴파일러 상태, 심벌 테이블, AST를 제어 흐름 그래프로 변환
2. 논리 오류나 코드 오류를 탐지, 실행 단계를 런타임 예외로부터 보호
## 어셈블리
컴파일 단계가 끝나면 블록 리스트 완성
각 프레임 블록은 명령 리스트와 다음 블록을 가리키는 포인터를 가짐
기본 프레임 블록들에 깊이 우선 탐색(DFS)를 실행, 명령들을 단일한 바이트 코드 시퀀스로 병합
10 changes: 5 additions & 5 deletions _sources/docs/8_0_eval_loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ CPython 에서 코드는 **평가 루프** 라는 개념을 통해 루프(Loop)

<br/>

![parse_tree](../images/8_eval_loop/01_eval1.jpg)
![parse_tree](../images/8_eval_loop/01_eval1.JPG)

다음은 이번 장을 통해 살펴볼 개념들입니다.
<br/>
Expand All @@ -40,7 +40,7 @@ CPython 에서 코드는 **평가 루프** 라는 개념을 통해 루프(Loop)



![CST_EXPR](../images/8_eval_loop/02_eval2.jpg)
![CST_EXPR](../images/8_eval_loop/02_eval2.JPG)
위의 PyThreadState는 _ts 의 별칭으로 사용되게 됩니다. 코드를 살펴보게 되면, 고유 식별자, 다른 스레드 상태와 연결된 연결 리스트, 인터프리터의 상태, 재귀 깊이 등을 나타내는 값들로 이루어져 있습니다.


Expand All @@ -56,17 +56,17 @@ AST 를 거쳐 컴파일된 객체는 프레임 객체에 최종적으로 삽입
<br/>
다음은 프레임 타입 객체 PyFrameObject 가 가진 프로퍼티입니다.

![Parser_Tokenizer](../images/8_eval_loop/03_eval3.jpg)
![Parser_Tokenizer](../images/8_eval_loop/03_eval3.JPG)

다음은 프로퍼티를 가진 CPython 의 코드 입니다.

![Parser_Tokenizer](../images/8_eval_loop/04_eval4.jpg)
![Parser_Tokenizer](../images/8_eval_loop/04_eval4.JPG)

인터프리터에 의해 만들어진 PyFrameObject 는 초기화 과정을 거치게 되는데, PyEval_EvalCode( ) 라는 함수 안에서 초기화 과정을 거치게 됩니다.

<br/>

![Parser_Tokenizer](../images/8_eval_loop/05_eval5.jpg)
![Parser_Tokenizer](../images/8_eval_loop/05_eval5.JPG)

<br/>
<br/>
Expand Down
Loading

0 comments on commit 7998b28

Please sign in to comment.