Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7935c5d
Initial plan
Copilot Nov 4, 2025
d667fae
Add WithPip and rename WithUvEnvironment to WithUv
Copilot Nov 4, 2025
d56a491
Update WithUv to bootstrap Python and expose custom args
Copilot Nov 4, 2025
ccbf1df
Unify WithPip and WithUv to follow Node.js pattern
Copilot Nov 4, 2025
aac0897
Remove unused resource files (PythonPipInstallerResource, PythonUvEnv…
Copilot Nov 4, 2025
1577270
Add support for detecting pyproject.toml
Copilot Nov 4, 2025
b3c803c
Make pip support both pyproject.toml and requirements.txt
Copilot Nov 4, 2025
b4b83d0
Add venv creator resource for pip package manager
Copilot Nov 4, 2025
51f1894
Refactor Python package management based on feedback
Copilot Nov 4, 2025
ba1f423
Add createIfNotExists parameter to WithVirtualEnvironment
Copilot Nov 4, 2025
0b0ba61
Refactor venv creation to use BeforeStartEvent and be directory-based
Copilot Nov 4, 2025
e51f072
Simplify venv creation to use per-resource child resources
Copilot Nov 4, 2025
8fc5073
Make venv creation independent of package manager
Copilot Nov 4, 2025
9c8026e
WIP: Refactor wait dependencies to use BeforeStartEvent (needs fixing)
Copilot Nov 5, 2025
b9f1ec1
Implement feedback: Rename to SetupDependencies, only run in RunMode,…
Copilot Nov 5, 2025
3a56b22
Fix failing tests - all 92 tests now passing
Copilot Nov 5, 2025
cf15d22
Use TryCreateResourceBuilder in RemoveVenvCreator
Copilot Nov 5, 2025
3b02a9c
Rename AddPythonScript to AddPythonApp with OverloadResolutionPriority
Copilot Nov 5, 2025
1a96d8b
Fix AddPythonScript/AddPythonApp naming - remove obsolete, rename cor…
Copilot Nov 5, 2025
2dc56ce
Fix build.
eerhardt Nov 5, 2025
14c5d98
Remove AddPythonScript
eerhardt Nov 5, 2025
d44a5dc
Fix WithUv default args - remove incomplete --python flag
Copilot Nov 5, 2025
f475d27
Fixed tests
davidfowl Nov 5, 2025
58fc26b
Minor PR feedback
eerhardt Nov 5, 2025
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
6 changes: 3 additions & 3 deletions playground/python/Python.AppHost/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@

var builder = DistributedApplication.CreateBuilder(args);

builder.AddPythonScript("script-only", "../script_only", "main.py");

Check failure on line 6 in playground/python/Python.AppHost/Program.cs

View check run for this annotation

Azure Pipelines / dotnet.aspire (Build Linux)

playground/python/Python.AppHost/Program.cs#L6

playground/python/Python.AppHost/Program.cs(6,1): error CS0618: (NETCORE_ENGINEERING_TELEMETRY=Build) 'PythonAppResourceBuilderExtensions.AddPythonScript(IDistributedApplicationBuilder, string, string, string)' is obsolete: 'Use AddPythonApp, AddPythonModule, or AddPythonExecutable instead for more explicit control over how the Python application is executed.'
builder.AddPythonScript("instrumented-script", "../instrumented_script", "main.py");

Check failure on line 7 in playground/python/Python.AppHost/Program.cs

View check run for this annotation

Azure Pipelines / dotnet.aspire (Build Linux)

playground/python/Python.AppHost/Program.cs#L7

playground/python/Python.AppHost/Program.cs(7,1): error CS0618: (NETCORE_ENGINEERING_TELEMETRY=Build) 'PythonAppResourceBuilderExtensions.AddPythonScript(IDistributedApplicationBuilder, string, string, string)' is obsolete: 'Use AddPythonApp, AddPythonModule, or AddPythonExecutable instead for more explicit control over how the Python application is executed.'

builder.AddPythonModule("fastapi-app", "../module_only", "uvicorn")
.WithArgs("api:app", "--reload", "--host=0.0.0.0", "--port=8000")
.WithHttpEndpoint(targetPort: 8000)
.WithUvEnvironment();
.WithUv();

// Run the same app on another port using uvicorn directly
builder.AddPythonExecutable("fastapi-uvicorn-app", "../module_only", "uvicorn")
Expand All @@ -27,11 +27,11 @@
c.Args.Add("--port=8002");
})
.WithHttpEndpoint(targetPort: 8002)
.WithUvEnvironment();
.WithUv();

// Uvicorn app using the AddUvicornApp method
builder.AddUvicornApp("uvicorn-app", "../uvicorn_app", "app:app")
.WithUvEnvironment()
.WithUv()
.WithExternalHttpEndpoints();

#if !SKIP_DASHBOARD_REFERENCE
Expand Down
2 changes: 1 addition & 1 deletion playground/python/flask_app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ This app is configured to run via the Python.AppHost project using `AddPythonMod
var flaskApp = builder.AddPythonModule("flask-app", "../flask_app", "flask")
.WithArgs("run", "--host=0.0.0.0", "--port=8000")
.WithHttpEndpoint(targetPort: 8000)
.WithUvEnvironment();
.WithUv();
```

## Local Development
Expand Down
472 changes: 422 additions & 50 deletions src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions src/Aspire.Hosting.Python/PythonEnvironmentAnnotation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@

namespace Aspire.Hosting.Python;

// Marker annotation to indicate a resource is for setting up a UV environment for a Python app.
// Marker annotation to indicate a resource is for setting up a Python environment.
internal class PythonEnvironmentAnnotation : IResourceAnnotation
{
public string? Version { get; set; }

public bool Uv { get; set; }

public VirtualEnvironment? VirtualEnvironment { get; set; }

/// <summary>
/// Gets or sets whether to automatically create the virtual environment if it doesn't exist.
/// Defaults to true.
/// </summary>
public bool CreateVenvIfNotExists { get; set; } = true;
}
21 changes: 21 additions & 0 deletions src/Aspire.Hosting.Python/PythonInstallCommandAnnotation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Hosting.ApplicationModel;

namespace Aspire.Hosting.Python;

/// <summary>
/// Represents the annotation for the Python package manager's install command.
/// </summary>
/// <param name="args">
/// The command line arguments for the Python package manager's install command.
/// This includes the command itself (i.e. "install", "sync").
/// </param>
internal sealed class PythonInstallCommandAnnotation(string[] args) : IResourceAnnotation
{
/// <summary>
/// Gets the command-line arguments supplied to the Python package manager.
/// </summary>
public string[] Args { get; } = args;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
namespace Aspire.Hosting.Python;

/// <summary>
/// A resource that represents a UV environment setup task for a Python application.
/// A resource that represents a Python package installer task for a Python application.
/// </summary>
/// <param name="name">The name of the resource.</param>
/// <param name="parent">The parent Python application resource.</param>
internal sealed class PythonUvEnvironmentResource(string name, PythonAppResource parent)
: ExecutableResource(name, "uv", parent.WorkingDirectory)
internal sealed class PythonInstallerResource(string name, PythonAppResource parent)
: ExecutableResource(name, "python", parent.WorkingDirectory)
{
}
17 changes: 17 additions & 0 deletions src/Aspire.Hosting.Python/PythonPackageInstallerAnnotation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Hosting.ApplicationModel;

namespace Aspire.Hosting.Python;

/// <summary>
/// Represents an annotation for a Python installer resource.
/// </summary>
internal sealed class PythonPackageInstallerAnnotation(ExecutableResource installerResource) : IResourceAnnotation
{
/// <summary>
/// The instance of the Installer resource used.
/// </summary>
public ExecutableResource Resource { get; } = installerResource;
}
18 changes: 18 additions & 0 deletions src/Aspire.Hosting.Python/PythonPackageManagerAnnotation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Hosting.ApplicationModel;

namespace Aspire.Hosting.Python;

/// <summary>
/// Represents the annotation for the Python package manager used in a resource.
/// </summary>
/// <param name="executableName">The name of the executable used to run the package manager.</param>
internal sealed class PythonPackageManagerAnnotation(string executableName) : IResourceAnnotation
{
/// <summary>
/// Gets the executable used to run the Python package manager.
/// </summary>
public string ExecutableName { get; } = executableName;
}
21 changes: 21 additions & 0 deletions src/Aspire.Hosting.Python/PythonVenvCreatorResource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Hosting.ApplicationModel;

namespace Aspire.Hosting.Python;

/// <summary>
/// A resource that represents a Python virtual environment creator task for a Python application.
/// </summary>
/// <param name="name">The name of the resource.</param>
/// <param name="parent">The parent Python application resource.</param>
/// <param name="venvPath">The path where the virtual environment should be created.</param>
internal sealed class PythonVenvCreatorResource(string name, PythonAppResource parent, string venvPath)
: ExecutableResource(name, "python", parent.WorkingDirectory)
{
/// <summary>
/// Gets the path where the virtual environment will be created.
/// </summary>
public string VenvPath => venvPath;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

#endif
var app = builder.AddUvicornApp("app", "./app", "main:app")
.WithUvEnvironment()
.WithUv()
.WithExternalHttpEndpoints()
#if UseRedisCache
.WithReference(cache)
Expand Down
Loading
Loading