KeiStory

CSnakes.Runtime 사용하기

 

CSnakes는 Python 코드와 라이브러리를 C#.NET 솔루션에 효율적으로 통합할 수 있게 해주는 강력한 도구입니다

py 파일에 정의된 function 을 C# 에서 직접 호출하여 사용이 가능합니다.

https://github.com/tonybaloney/csnakes

 

GitHub - tonybaloney/CSnakes: Embed Python in .NET

Embed Python in .NET. Contribute to tonybaloney/CSnakes development by creating an account on GitHub.

github.com

 

주요 특징

.NET 8과 9 지원: 최신 .NET 버전과 호환됩니다.

Python 3.9-3.13 지원: 다양한 Python 버전을 지원합니다.

크로스 플랫폼: Windows, macOS, Linux에서 사용 가능합니다.

가상 환경 및 C-확장 지원: Python의 가상 환경과 C-확장을 사용할 수 있습니다.

 

PythonLibrary 프로젝트 생성 (.py)

먼저 py 파일이 만들어질 프로젝트를 생성합니다. 클래스라이브러리 프로젝트를 만들고 클래스파일을 하나 추가해 main.py 로 변경합니다

아래와 같이 코드합니다.

def hello_world(name: str) -> str:
    return f"Hello, {name}!"

 

CSnakes.Runtime Nuget package 를 설치합니다.

 

 

Console 프로젝트

Console 프로젝트를 만들어서 앞서 만든 PythonLibrary 에 있는 main.py 의 function 를 호출합니다.

먼저 앞서 만들 프로젝트를 참조합니다.

CSnakes.Runtime Nuget package 를 설치합니다.

python 이 필요한데 여기에서는 Nuget 을 이용한 방법을 알아봅니다.

(Nuget 이 아닌 로컬에 설치된 python 경로를 지정해도 됩니다)

python Nuget package 를 설치합니다.

using CSnakes.Runtime;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

// NUGET 패키지 이용
var builder = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        // py 파일이 있는 경로
        var home = Path.Join(Path.GetDirectoryName(Environment.ProcessPath));
        var venv = Path.Join(home, ".venv");

        services
        .WithPython()
        .WithHome(home)
        .WithVirtualEnvironment(venv)
        .FromNuGet("3.13.1")
        .WithPipInstaller();
    });

var app = builder.Build();

var env = app.Services.GetRequiredService<IPythonEnvironment>();

// Python 환경 정보 출력
Console.WriteLine($"Python Version: {env.Version}");

Console.WriteLine(env.Main().HelloWorld("CSnake"));
Console.ReadKey();

HelloWorld 를 F12 로 정의로 가면 아래처럼 main.py 파일이 c# 코드로 변환된 걸 볼수 있습니다.

// <auto-generated/>
#nullable enable

using CSnakes.Runtime;
using CSnakes.Runtime.Python;

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection.Metadata;

using Microsoft.Extensions.Logging;

[assembly: MetadataUpdateHandler(typeof(CSnakes.Runtime.MainExtensions))]

namespace CSnakes.Runtime;

public static class MainExtensions
{
    private static IMain? instance;

    private static ReadOnlySpan<byte> HotReloadHash => "480beb1659a5a7a5a15d4f3b53dcaec1"u8;

    public static IMain Main(this IPythonEnvironment env)
    {
        if (instance is null)
        {
            instance = new MainInternal(env.Logger);
        }
        Debug.Assert(!env.IsDisposed());
        return instance;
    }

    public static void UpdateApplication(Type[]? updatedTypes)
    {
        instance?.ReloadModule();
    }

    private class MainInternal : IMain
    {
        private PyObject module;
        private readonly ILogger<IPythonEnvironment> logger;

        private PyObject __func_hello_world;

        internal MainInternal(ILogger<IPythonEnvironment> logger)
        {
            this.logger = logger;
            using (GIL.Acquire())
            {
                logger.LogDebug("Importing module {ModuleName}", "main");
                module = Import.ImportModule("main");
                this.__func_hello_world = module.GetAttr("hello_world");
            }
        }

        void IReloadableModuleImport.ReloadModule()
        {
            logger.LogDebug("Reloading module {ModuleName}", "main");
            using (GIL.Acquire())
            {
                Import.ReloadModule(ref module);
                // Dispose old functions
                this.__func_hello_world.Dispose();
                // Bind to new functions
                this.__func_hello_world = module.GetAttr("hello_world");
            }
        }

        public void Dispose()
        {
            logger.LogDebug("Disposing module {ModuleName}", "main");
            this.__func_hello_world.Dispose();
            module.Dispose();
        }

        public string HelloWorld(string name)
        {
            using (GIL.Acquire())
            {
                logger.LogDebug("Invoking Python function: {FunctionName}", "hello_world");
                PyObject __underlyingPythonFunc = this.__func_hello_world;
                using PyObject name_pyObject = PyObject.From(name)!;
                using PyObject __result_pyObject = __underlyingPythonFunc.Call(name_pyObject);
                return __result_pyObject.As<string>();
            }
        }
    }
}

/// <summary>
/// Represents functions of the Python module <c>main</c>.
/// </summary>
public interface IMain : IReloadableModuleImport
{
    /// <summary>
    /// Invokes the Python function <c>hello_world</c>:
    /// <code><![CDATA[
    /// def hello_world(name: str) -> str: ...
    /// ]]></code>
    /// </summary>
    string HelloWorld(string name);
}

실행 결과

 

반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band