Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Uvicorn Integration #219

Merged
merged 24 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
1b0bfdc
adds uvicorn integration
tommasodotNET Nov 9, 2024
d42404f
working example for uvicorn
tommasodotNET Nov 10, 2024
a8ab95d
adds tests
tommasodotNET Nov 11, 2024
19463db
adds uvicorn to CI
tommasodotNET Nov 11, 2024
15ed4d0
adds virtual environment support
tommasodotNET Nov 11, 2024
b49ab85
Update src/Aspire.CommunityToolkit.Hosting.Uvicorn/CommunityToolkit.A…
tommasodotNET Nov 11, 2024
7388f30
adds python/uvicorn tasks to main and release ci
tommasodotNET Nov 11, 2024
bb85a5c
Merge branch '207-uvicorn' of github.com:CommunityToolkit/Aspire into…
tommasodotNET Nov 11, 2024
a842cfb
Update src/Aspire.CommunityToolkit.Hosting.Uvicorn/CommunityToolkit.A…
tommasodotNET Nov 12, 2024
94bd1e8
Update src/Aspire.CommunityToolkit.Hosting.Uvicorn/UvicornAppHostingE…
tommasodotNET Nov 12, 2024
1a07bdc
adds test on working directory
tommasodotNET Nov 12, 2024
33a7b8e
Merge branch '207-uvicorn' of github.com:CommunityToolkit/Aspire into…
tommasodotNET Nov 12, 2024
bde4d61
Merge branch 'main' into 207-uvicorn
aaronpowell Nov 13, 2024
389b47d
Moving to a Python.Extensions package rather than uvicorn specific
aaronpowell Nov 13, 2024
af444a0
Using shared pathutils
aaronpowell Nov 13, 2024
627befa
Fixing build issue
aaronpowell Nov 13, 2024
dd52f90
forgot how it restructures everything so using the util method
aaronpowell Nov 13, 2024
e144dfb
Merge remote-tracking branch 'origin/main' into 207-uvicorn
aaronpowell Nov 13, 2024
7b35d89
Update src/CommunityToolkit.Aspire.Hosting.Python.Extensions/README.md
tommasodotNET Nov 13, 2024
198afaf
Update src/CommunityToolkit.Aspire.Hosting.Python.Extensions/README.md
tommasodotNET Nov 13, 2024
357beea
Merge branch '207-uvicorn' of https://github.com/CommunityToolkit/Asp…
aaronpowell Nov 13, 2024
5efb71d
Basing ourselves on the Python hosting integration
aaronpowell Nov 13, 2024
3cc5012
Ignoring Aspire.Hosting.Python from code coverage
aaronpowell Nov 13, 2024
7afdd82
Merge branch 'main' into 207-uvicorn
aaronpowell Nov 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/dotnet-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ jobs:
dotnet-version: |
8.0.x
9.0.x
- name: Set up Python
tommasodotNET marked this conversation as resolved.
Show resolved Hide resolved
uses: actions/setup-python@v4
with:
python-version: "3.11"
- name: Install Uvicorn
run: |
python -m pip install --upgrade pip
pip install uvicorn
- uses: actions/setup-go@v5
name: Set up Go
with:
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/dotnet-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ jobs:
dotnet-version: |
${{ env.DEFAULT_DOTNET_VERSION }}
9.0.x
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.11"
- name: Install Uvicorn
run: |
python -m pip install --upgrade pip
pip install uvicorn
- uses: actions/setup-go@v5
name: Set up Go
with:
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/dotnet-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ jobs:
dotnet-version: |
${{ env.DEFAULT_DOTNET_VERSION }}
9.0.x
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.11"
- name: Install Uvicorn
run: |
python -m pip install --upgrade pip
pip install uvicorn
- uses: actions/setup-go@v5
name: Set up Go
with:
Expand Down
24 changes: 24 additions & 0 deletions CommunityToolkit.Aspire.sln
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests-app-hosts", "tests-ap
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ollama.AppHost", "tests-app-hosts\Ollama.AppHost\Ollama.AppHost.csproj", "{2CC61B84-CF97-4CE7-A08F-2EECF4AEAD92}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Aspire.Hosting.Python.Extensions", "src\CommunityToolkit.Aspire.Hosting.Python.Extensions\CommunityToolkit.Aspire.Hosting.Python.Extensions.csproj", "{4DCF987E-9071-4899-8B5F-5FDAF2BC77D3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "python", "python", "{DDDAABA3-D8F0-47C6-98E0-AB57F28404CF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Aspire.Hosting.Python.Extensions.AppHost", "examples\python\CommunityToolkit.Aspire.Hosting.Python.Extensions.AppHost\CommunityToolkit.Aspire.Hosting.Python.Extensions.AppHost.csproj", "{C686CEA0-8B89-470B-84A2-0264040DCDC8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Aspire.Hosting.Python.Extensions.Tests", "tests\CommunityToolkit.Aspire.Hosting.Python.Extensions.Tests\CommunityToolkit.Aspire.Hosting.Python.Extensions.Tests.csproj", "{5B825CF9-E8B8-4960-9330-648ED0323FE0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -311,6 +319,18 @@ Global
{C7D057AF-E2A5-4E26-846E-A328A0F14A3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7D057AF-E2A5-4E26-846E-A328A0F14A3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7D057AF-E2A5-4E26-846E-A328A0F14A3C}.Release|Any CPU.Build.0 = Release|Any CPU
{4DCF987E-9071-4899-8B5F-5FDAF2BC77D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4DCF987E-9071-4899-8B5F-5FDAF2BC77D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4DCF987E-9071-4899-8B5F-5FDAF2BC77D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4DCF987E-9071-4899-8B5F-5FDAF2BC77D3}.Release|Any CPU.Build.0 = Release|Any CPU
{C686CEA0-8B89-470B-84A2-0264040DCDC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C686CEA0-8B89-470B-84A2-0264040DCDC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C686CEA0-8B89-470B-84A2-0264040DCDC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C686CEA0-8B89-470B-84A2-0264040DCDC8}.Release|Any CPU.Build.0 = Release|Any CPU
{5B825CF9-E8B8-4960-9330-648ED0323FE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5B825CF9-E8B8-4960-9330-648ED0323FE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5B825CF9-E8B8-4960-9330-648ED0323FE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5B825CF9-E8B8-4960-9330-648ED0323FE0}.Release|Any CPU.Build.0 = Release|Any CPU
{6BC98146-279F-4DE5-9B6E-0F0C07507421}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6BC98146-279F-4DE5-9B6E-0F0C07507421}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6BC98146-279F-4DE5-9B6E-0F0C07507421}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -389,6 +409,10 @@ Global
{4AE83D68-EA10-473D-BD26-19C5928A8620} = {8519CC01-1370-47C8-AD94-B0F326B1563F}
{79EF8E85-1DFC-42B5-BDE3-72639F25848C} = {4AE83D68-EA10-473D-BD26-19C5928A8620}
{C7D057AF-E2A5-4E26-846E-A328A0F14A3C} = {899F0713-7FC6-4750-BAFC-AC650B35B453}
{4DCF987E-9071-4899-8B5F-5FDAF2BC77D3} = {414151D4-7009-4E78-A5C6-D99EBD1E67D1}
{DDDAABA3-D8F0-47C6-98E0-AB57F28404CF} = {8519CC01-1370-47C8-AD94-B0F326B1563F}
{C686CEA0-8B89-470B-84A2-0264040DCDC8} = {DDDAABA3-D8F0-47C6-98E0-AB57F28404CF}
{5B825CF9-E8B8-4960-9330-648ED0323FE0} = {899F0713-7FC6-4750-BAFC-AC650B35B453}
{6BC98146-279F-4DE5-9B6E-0F0C07507421} = {414151D4-7009-4E78-A5C6-D99EBD1E67D1}
{662514C8-EAED-4EAB-91CE-893D4DE2469A} = {8519CC01-1370-47C8-AD94-B0F326B1563F}
{1E753568-E34B-4E93-93F8-43764171725D} = {662514C8-EAED-4EAB-91CE-893D4DE2469A}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="Aspire.AppHost.Sdk" Version="$(AspireAppHostSdkVersion)"/>

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
<UserSecretsId>5a6548a5-b5dd-40f0-876a-9e3d4ac91fd1</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\CommunityToolkit.Aspire.Hosting.Python.Extensions\CommunityToolkit.Aspire.Hosting.Python.Extensions.csproj" IsAspireProjectResource="false" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
var builder = DistributedApplication.CreateBuilder(args);

var uvicorn = builder.AddUvicornApp("uvicornapp", "../uvicornapp-api", "main:app")
.WithHttpEndpoint(env: "UVICORN_PORT");

builder.Build().Run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:17247;http://localhost:15071",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21120",
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22298"
}
},
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:15071",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19155",
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20207"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Aspire.Hosting.Dcp": "Warning"
}
}
}
2 changes: 2 additions & 0 deletions examples/python/uvicornapp-api/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__pycache__
.venv
26 changes: 26 additions & 0 deletions examples/python/uvicornapp-api/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import os
import asyncio
import uvicorn

async def app(scope, receive, send):
assert scope['type'] == 'http'

await send({
'type': 'http.response.start',
'status': 200,
'headers': [
[b'content-type', b'text/plain'],
],
})
await send({
'type': 'http.response.body',
'body': b'Hello, world!',
})

async def main():
config = uvicorn.Config("main:app", log_level="info")
server = uvicorn.Server(config)
await server.serve()

if __name__ == "__main__":
asyncio.run(main())
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AdditionalPackageTags>hosting uvicorn python</AdditionalPackageTags>
<Description>A .NET Aspire integration for hosting Uvicorn apps.</Description>
</PropertyGroup>

<ItemGroup>
<Compile Include="$(SharedDir)\PathNormalizer.cs" Link="Utils\PathNormalizer.cs" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#nullable enable
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#nullable enable
Aspire.Hosting.ApplicationModel.UvicornAppResource
Aspire.Hosting.ApplicationModel.UvicornAppResource.UvicornAppResource(string! name, string! workingDirectory) -> void
Aspire.Hosting.UvicornAppHostingExtension
static Aspire.Hosting.UvicornAppHostingExtension.AddUvicornApp(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name, string! projectDirectory, string! appName, string![]? args = null) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.UvicornAppResource!>!
35 changes: 35 additions & 0 deletions src/CommunityToolkit.Aspire.Hosting.Python.Extensions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# CommunityToolkit.Aspire.Hosting.Python.Extensions library

Provides extensions methods and resource definitions for the .NET Aspire AppHost to support running Uvicorn applications.

## Getting Started

### Install the package

In your AppHost project, install the package using the following command:

```dotnetcli
dotnet add package CommunityToolkit.Aspire.Hosting.Python.Extensions
```

### Initialize the Python virtual environment

Please refer to the [Python virtual environment](https://learn.microsoft.com/en-us/dotnet/aspire/get-started/build-aspire-apps-with-python?tabs=powershell#initialize-the-python-virtual-environment) section for more information.
tommasodotNET marked this conversation as resolved.
Show resolved Hide resolved

### Example usage

Then, in the _Program.cs_ file of `AddUvicornApp`, define a Uvicorn resource, then call `Add`:

```csharp
var uvicorn = builder.AddUvicornApp("uvicornapp", "../uvicornapp-api", "main:app")
.WithHttpEndpoint(env: "UVICORN_PORT");
```

## Additional Information

https://learn.microsoft.com/dotnet/aspire/community-toolkit/hosting-uvicorn
tommasodotNET marked this conversation as resolved.
Show resolved Hide resolved

## Feedback & contributing

https://github.com/CommunityToolkit/Aspire

Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using Aspire.Hosting.ApplicationModel;
using CommunityToolkit.Aspire.Hosting.Golang.Utils;

Check failure on line 2 in src/CommunityToolkit.Aspire.Hosting.Python.Extensions/UvicornAppHostingExtension.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

The type or namespace name 'Hosting' does not exist in the namespace 'CommunityToolkit.Aspire' (are you missing an assembly reference?)

Check failure on line 2 in src/CommunityToolkit.Aspire.Hosting.Python.Extensions/UvicornAppHostingExtension.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

The type or namespace name 'Hosting' does not exist in the namespace 'CommunityToolkit.Aspire' (are you missing an assembly reference?)

namespace Aspire.Hosting;

/// <summary>
/// Provides extension methods for adding Uvicorn applications to an <see cref="IDistributedApplicationBuilder"/>.
/// </summary>
public static class UvicornAppHostingExtension
{
/// <summary>
/// Adds a Uvicorn application to the distributed application builder.
/// </summary>
/// <param name="builder">The distributed application builder.</param>
/// <param name="name">The name of the Uvicorn application.</param>
/// <param name="projectDirectory">The directory of the project containing the Uvicorn application.</param>
/// <param name="appName">The name of the uvicorn app.</param>
/// <param name="scriptArgs">Optional arguments to pass to the script.</param>
/// <returns>An <see cref="IResourceBuilder{UvicornAppResource}"/> for the Uvicorn application resource.</returns>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="builder"/> is null.</exception>
public static IResourceBuilder<UvicornAppResource> AddUvicornApp(
this IDistributedApplicationBuilder builder,
[ResourceName] string name,
string projectDirectory,
string appName,
string[]? args = null)
{
ArgumentNullException.ThrowIfNull(builder);

return builder.AddUvicornApp(name, projectDirectory, appName, ".venv", args);
}

private static IResourceBuilder<UvicornAppResource> AddUvicornApp(this IDistributedApplicationBuilder builder,
string name,
string projectDirectory,
string appName,
string virtualEnvironmentPath,
string[]? args = null)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(appName);

string wd = projectDirectory ?? Path.Combine("..", name);

projectDirectory = PathNormalizer.NormalizePathForCurrentPlatform(Path.Combine(builder.AppHostDirectory, wd));

var virtualEnvironment = new VirtualEnvironment(Path.IsPathRooted(virtualEnvironmentPath)
? virtualEnvironmentPath
: Path.Join(projectDirectory, virtualEnvironmentPath));

var instrumentationExecutable = virtualEnvironment.GetExecutable("opentelemetry-instrument");

string[] allArgs = args is { Length: > 0 }
? [appName, .. args]
: [appName];

var projectResource = new UvicornAppResource(name, projectDirectory);

var resourceBuilder = builder.AddResource(projectResource)
.WithArgs(allArgs)
.WithArgs(context =>
{
// If the project is to be automatically instrumented, add the instrumentation executable arguments first.
if (!string.IsNullOrEmpty(instrumentationExecutable))
{
AddOpenTelemetryArguments(context);

// // Add the python executable as the next argument so we can run the project.
// context.Args.Add(pythonExecutable!);
}
});

if (!string.IsNullOrEmpty(instrumentationExecutable))
{
resourceBuilder.WithOtlpExporter();

// Make sure to attach the logging instrumentation setting, so we can capture logs.
// Without this you'll need to configure logging yourself. Which is kind of a pain.
resourceBuilder.WithEnvironment("OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED", "true");
}

return resourceBuilder;
}

private static void AddOpenTelemetryArguments(CommandLineArgsCallbackContext context)
{
context.Args.Add("--traces_exporter");
context.Args.Add("otlp");

context.Args.Add("--logs_exporter");
context.Args.Add("console,otlp");

context.Args.Add("--metrics_exporter");
context.Args.Add("otlp");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

namespace Aspire.Hosting.ApplicationModel;

public class UvicornAppResource(string name, string workingDirectory)
: ExecutableResource(name, "uvicorn", workingDirectory), IResourceWithServiceDiscovery;
Loading
Loading