PyO3를 사용한 Rust 기반의 파이썬 배포 파일 생성하기

많은 개발자들이 Python과 Rust의 강력한 기능을 결합하여 놀라운 애플리케이션을 개발하고 있습니다. PyO3는 이러한 결합을 더욱 간편하게 만들어주는 Rust와 CPython 사이의 다리 역할을 하는 라이브러리입니다.

이번 블로그 포스트에서는 PyO3를 사용하여 Rust로 작성한 코드를 패키징하여 파이썬 배포 파일을 생성하는 방법에 대해 알아보겠습니다.

1. Rust 환경 설정하기

먼저, Rust 개발 환경을 설정해야 합니다. Rust 홈페이지(https://www.rust-lang.org/)에서 제공하는 설치 파일을 다운로드하고, 설치 과정을 완료하세요.

설치가 완료되면, 터미널 또는 명령 프롬프트를 열고 다음 명령어를 실행하여 rustup을 업데이트합니다.

$ rustup update

그리고 다음 명령어를 실행하여 최신 버전의 Rust 컴파일러를 설치합니다.

$ rustup install stable

2. PyO3 의존성 추가하기

Rust 환경이 설정되었으므로, 이제 PyO3 의존성을 프로젝트에 추가해야 합니다. 프로젝트 디렉토리로 이동한 다음, Cargo.toml 파일을 열고 다음 코드를 추가하세요.

[dependencies]
pyo3 = "0.14"

의존성을 추가하고 나면, 다음 명령어를 실행하여 의존성을 다운로드합니다.

$ cargo fetch

3. Rust로 코드 작성하기

이제 Rust로 코드를 작성해보겠습니다. 프로젝트 디렉토리에 src 폴더를 만들고, main.rs 파일을 생성합니다. 그리고 다음 코드를 main.rs 파일에 추가하세요.

use pyo3::prelude::*;
use pyo3::types::IntoPyDict;

#[pymodule]
fn my_module(py: Python, m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(hello, m)?)?;
    m.add_function(wrap_pyfunction!(add, m)?)?;
    Ok(())
}

#[pyfunction]
fn hello() -> PyResult<String> {
    Ok("Hello, PyO3!".to_string())
}

#[pyfunction]
fn add(a: usize, b: usize) -> PyResult<usize> {
    Ok(a + b)
}

#[pymodule]
fn my_package(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(hello, m)?)?;
    m.add_function(wrap_pyfunction!(add, m)?)?;
    Ok(())
}

#[pymodule]
fn rust_module(py: Python, m: &PyModule) -> PyResult<()> {
    m.add_submodule(py, my_package)?;
    Ok(())
}

#[pymodule]
fn my_rust_lib(py: Python, m: &PyModule) -> PyResult<()> {
    m.add_submodule(py, rust_module)?;
    Ok(())
}

#[pymodule]
fn my_rust_package(py: Python, m: &PyModule) -> PyResult<()> {
    m.add_submodule(py, my_rust_lib)?;
    Ok(())
}

#[pymodule]
fn my_rust_package_wrapper(py: Python, m: &PyModule) -> PyResult<()> {
    m.add_submodule(py, my_rust_package)?;
    Ok(())
}

#[pymodule]
fn pyo3_util_module(py: Python, m: &PyModule) -> PyResult<()> {
    m.add_submodule(py, my_module)?;
    Ok(())
}

#[pymodule]
fn pyo3_util(py: Python, m: &PyModule) -> PyResult<()> {
    m.add_module(pyo3_util_module)?;
    Ok(())
}

#[pymodule]
fn my_python_package_wrapper(py: Python, m: &PyModule) -> PyResult<()> {
    m.add_submodule(py, pyo3_util)?;
    Ok(())
}

pyo3::create_module!(my_python_package, my_python_package_wrapper);

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_hello() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let sys = py.import("sys").expect("Failed to import sys");
        let sys_modules: &PyDict = sys.get_dict(py).expect("Failed to get sys modules dict");
        let my_module = my_python_package_wrapper(py).expect("Failed to create my_python_package_wrapper");
        sys_modules.set_item(py, "my_module", my_module).expect("Failed to set my_module");
        let result: String = py
            .eval("my_module.hello()", Some(sys_modules), None)
            .expect("Failed to evaluate code")
            .extract()
            .expect("Failed to extract result");
        assert_eq!(result, "Hello, PyO3!");
    }

    #[test]
    fn test_add() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let sys = py.import("sys").expect("Failed to import sys");
        let sys_modules: &PyDict = sys.get_dict(py).expect("Failed to get sys modules dict");
        let my_module = my_python_package_wrapper(py).expect("Failed to create my_python_package_wrapper");
        sys_modules.set_item(py, "my_module", my_module).expect("Failed to set my_module");
        let result: usize = py
            .eval("my_module.add(3, 4)", Some(sys_modules), None)
            .expect("Failed to evaluate code")
            .extract()
            .expect("Failed to extract result");
        assert_eq!(result, 7);
    }
}

4. 파이썬으로 빌드하기

Rust 코드를 작성했으므로, 이제 PyO3를 사용하여 파이썬 배포 파일을 생성할 차례입니다. 터미널 또는 명령 프롬프트에서 다음 명령어를 실행하여 배포 파일을 생성하세요.

$ cargo build --release --features "extension-module"

빌드가 완료되면, target 폴더에 libmy_python_package.so (Linux), my_python_package.dll (Windows), libmy_python_package.dylib (macOS)와 같은 파일이 생성됩니다.

5. 파이썬에서 사용하기

빌드된 파일을 파이썬에서 사용할 수 있도록 환경을 설정해야 합니다. 프로젝트 디렉토리에 __init__.py 파일을 생성하고, 다음 코드를 추가하세요.

from .my_python_package import my_module

__all__ = ['my_module']

이제 파이썬에서 다음과 같이 모듈을 import 할 수 있습니다.

from my_python_package import my_module

print(my_module.hello()) # Output: Hello, PyO3!
print(my_module.add(3, 4)) # Output: 7

이제 PyO3를 사용하여 Rust로 작성한 코드를 패키징하여 파이썬 배포 파일을 생성하는 방법에 대해 알아보았습니다. PyO3를 사용하면 Rust와 Python의 강력한 기능을 쉽게 결합하여 놀라운 애플리케이션을 개발할 수 있습니다. 더 자세한 정보는 PyO3 공식 문서를 참조하세요.

참조:

해시태그: #PyO3 #Rust #Python