Skip to content

Commit

Permalink
first pass at updating "PythonProjectResource" to "PythonAppResource"
Browse files Browse the repository at this point in the history
  • Loading branch information
maddymontaquila committed Sep 18, 2024
1 parent 9385815 commit ae6dad1
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 58 deletions.
4 changes: 2 additions & 2 deletions playground/python/Python.AppHost/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

var builder = DistributedApplication.CreateBuilder(args);

builder.AddPythonProject("script-only", "../script_only", "main.py");
builder.AddPythonProject("instrumented-script", "../instrumented_script", "main.py");
builder.AddPythonApp("script-only", "../script_only", "main.py");
builder.AddPythonApp("instrumented-script", "../instrumented_script", "main.py");

builder.Build().Run();
10 changes: 5 additions & 5 deletions src/Aspire.Hosting.Python/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#nullable enable
Aspire.Hosting.Python.PythonProjectResource
Aspire.Hosting.Python.PythonProjectResource.PythonProjectResource(string! name, string! executablePath, string! projectDirectory) -> void
Aspire.Hosting.PythonProjectResourceBuilderExtensions
static Aspire.Hosting.PythonProjectResourceBuilderExtensions.AddPythonProject(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name, string! projectDirectory, string! scriptPath, params string![]! scriptArgs) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Python.PythonProjectResource!>!
static Aspire.Hosting.PythonProjectResourceBuilderExtensions.AddPythonProject(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name, string! projectDirectory, string! scriptPath, string! virtualEnvironmentPath, params string![]! scriptArgs) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Python.PythonProjectResource!>!
Aspire.Hosting.Python.PythonAppResource
Aspire.Hosting.Python.PythonAppResource.PythonAppResource(string! name, string! executablePath, string! projectDirectory) -> void
Aspire.Hosting.PythonAppResourceBuilderExtensions
static Aspire.Hosting.PythonAppResourceBuilderExtensions.AddPythonApp(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name, string! projectDirectory, string! scriptPath, params string![]! scriptArgs) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Python.PythonAppResource!>!
static Aspire.Hosting.PythonAppResourceBuilderExtensions.AddPythonApp(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name, string! projectDirectory, string! scriptPath, string! virtualEnvironmentPath, params string![]! scriptArgs) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Python.PythonAppResource!>!
21 changes: 21 additions & 0 deletions src/Aspire.Hosting.Python/PythonAppResource.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 System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Aspire.Hosting.ApplicationModel;

namespace Aspire.Hosting.Python;

/// <summary>
/// A resource that represents a python executible or app.
/// </summary>
/// <param name="name">The name of the resource.</param>
/// <param name="executablePath">The path to the executable used to run the python app.</param>
/// <param name="projectDirectory">The path to the directory containing the python app.</param>
public class PythonProjectResource(string name, string executablePath, string projectDirectory)
: ExecutableResource(ThrowIfNull(name), ThrowIfNull(executablePath), ThrowIfNull(projectDirectory)), IResourceWithServiceDiscovery
{
private static string ThrowIfNull([NotNull] string? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null)
=> argument ?? throw new ArgumentNullException(paramName);
}
180 changes: 180 additions & 0 deletions src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// 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;
using Aspire.Hosting.Python;
using Aspire.Hosting.Utils;

namespace Aspire.Hosting;

/// <summary>
/// Provides extension methods for adding Python applications to an <see cref="IDistributedApplicationBuilder"/>.
/// </summary>
public static class PythonAppResourceBuilderExtensions
{
/// <summary>
/// Adds a python application with a virtual environment to the application model.
/// </summary>
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/> to add the resource to.</param>
/// <param name="name">The name of the resource.</param>
/// <param name="projectDirectory">The path to the directory containing the python app files.</param>
/// <param name="scriptPath">The path to the script relative to the project directory to run.</param>
/// <param name="scriptArgs">The arguments for the script.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/>.</returns>
/// <remarks>
/// <para>
/// The virtual environment must be initialized before running the project. By default the virtual environment folder is expected
/// to be named <c>.venv</c> and be located in the project directory. If the virtual environment is located in a different directory
/// this default can be specified by using the <see cref="AddPythonProject(IDistributedApplicationBuilder, string, string, string, string, string[])"/>
/// overload of this method.
/// </para>
/// <para>
/// The virtual environment is setup individually for each project to allow each project to use a different version of
/// Python and dependencies. To setup a virtual environment use the <c>python -m venv .venv</c> command in the project
/// directory. This will create a virtual environment in the <c>.venv</c> directory.
/// </para>
/// <para>
/// To restore dependencies in the virtual environment first activate the environment by executing the activation
/// script and then use the <c>pip install -r requirements.txt</c> command to restore dependencies.
/// </para>
/// <para>
/// To receive traces, logs, and metrics from the python project in the dashboard, the project must be instrumented with OpenTelemetry.
/// You can instrument your project by adding the <c>opentelemetry-distro</c>, and <c>opentelemetry-exporter-otlp</c> to
/// your Python project.
/// </para>
/// </remarks>
/// <example>
/// Add a python project to the application model. In this example the project is located in the <c>PythonProject</c> directory
/// if this path is relative then it is assumed to be relative to the AppHost directory, and the virtual enviroment path if relative
/// is relative to the project directory. In the example below, if the app host directory is <c>$HOME/repos/MyApp/src/MyApp.AppHost</c> then
/// the ProjectPath would be <c>$HOME/repos/MyApp/src/MyApp.AppHost/PythonProject</c> and the virtual environment path (defaulted) would
/// be <c>$HOME/repos/MyApp/src/MyApp.AppHost/PythonProject/.venv</c>.
/// <code lang="csharp">
/// var builder = DistributedApplication.CreateBuilder(args);
///
/// builder.AddPythonApp("python-project", "PythonProject", "main.py");
///
/// builder.Build().Run();
/// </code>
/// </example>
public static IResourceBuilder<PythonAppResource> AddPythonApp(

Check failure on line 60 in src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / dotnet.aspire (Build Linux)

src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs#L60

src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs(60,36): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'PythonAppResource' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 60 in src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / dotnet.aspire (Build Linux_helix_tests)

src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs#L60

src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs(60,36): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'PythonAppResource' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 60 in src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / dotnet.aspire

src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs#L60

src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs(60,36): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'PythonAppResource' could not be found (are you missing a using directive or an assembly reference?)
this IDistributedApplicationBuilder builder, string name, string projectDirectory, string scriptPath, params string[] scriptArgs)
{
ArgumentNullException.ThrowIfNull(builder);

return builder.AddPythonApp(name, projectDirectory, scriptPath, ".venv", scriptArgs);
}

/// <summary>
/// Adds a python application with a virtual environment to the application model.
/// </summary>
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/> to add the resource to.</param>
/// <param name="name">The name of the resource.</param>
/// <param name="projectDirectory">The path to the directory containing the python project files.</param>
/// <param name="scriptPath">The path to the script relative to the project directory to run.</param>
/// <param name="virtualEnvironmentPath">Path to the virtual environment.</param>
/// <param name="scriptArgs">The arguments for the script.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/>.</returns>
/// <remarks>
/// <para>
/// The virtual environment is setup individually for each project to allow each project to use a different version of
/// Python and dependencies. To setup a virtual environment use the <c>python -m venv .venv</c> command in the project
/// directory. This will create a virtual environment in the <c>.venv</c> directory (where <c>.venv</c> is the name of your
/// virtual environment directory).
/// </para>
/// <para>
/// To restore dependencies in the virtual environment first activate the environment by executing the activation
/// script and then use the <c>pip install -r requirements.txt</c> command to restore dependencies.
/// </para>
/// <para>
/// To receive traces, logs, and metrics from the python project in the dashboard, the project must be instrumented with OpenTelemetry.
/// You can instrument your project by adding the <c>opentelemetry-distro</c>, and <c>opentelemetry-exporter-otlp</c> to
/// your Python project.
/// </para>
/// </remarks>
/// <example>
/// Add a python project to the application model. In this example the project is located in the <c>PythonProject</c> directory
/// if this path is relative then it is assumed to be relative to the AppHost directory, and the virtual enviroment path if relative
/// is relative to the project directory. In the example below, if the app host directory is <c>$HOME/repos/MyApp/src/MyApp.AppHost</c> then
/// the ProjectPath would be <c>$HOME/repos/MyApp/src/MyApp.AppHost/PythonProject</c> and the virtual environment path (defaulted) would
/// be <c>$HOME/repos/MyApp/src/MyApp.AppHost/PythonProject/.venv</c>.
/// <code lang="csharp">
/// var builder = DistributedApplication.CreateBuilder(args);
///
/// builder.AddPythonApp("python-project", "PythonProject", "main.py");
///
/// builder.Build().Run();
/// </code>
/// </example>
public static IResourceBuilder<PythonAppResource> AddPythonApp(

Check failure on line 109 in src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / dotnet.aspire (Build Linux)

src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs#L109

src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs(109,36): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'PythonAppResource' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 109 in src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / dotnet.aspire (Build Linux_helix_tests)

src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs#L109

src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs(109,36): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'PythonAppResource' could not be found (are you missing a using directive or an assembly reference?)
this IDistributedApplicationBuilder builder, string name, string projectDirectory, string scriptPath,
string virtualEnvironmentPath, params string[] scriptArgs)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(name);
ArgumentNullException.ThrowIfNull(projectDirectory);
ArgumentNullException.ThrowIfNull(scriptPath);
ArgumentNullException.ThrowIfNull(virtualEnvironmentPath);
ArgumentNullException.ThrowIfNull(scriptArgs);

projectDirectory = PathNormalizer.NormalizePathForCurrentPlatform(Path.Combine(builder.AppHostDirectory, projectDirectory));
var virtualEnvironment = new VirtualEnvironment(Path.IsPathRooted(virtualEnvironmentPath)
? virtualEnvironmentPath
: Path.Join(projectDirectory, virtualEnvironmentPath));

var instrumentationExecutable = virtualEnvironment.GetExecutable("opentelemetry-instrument");
var pythonExecutable = virtualEnvironment.GetRequiredExecutable("python");
var projectExecutable = instrumentationExecutable ?? pythonExecutable;

var projectResource = new PythonProjectResource(name, projectExecutable, projectDirectory);

var resourceBuilder = builder.AddResource(projectResource).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!);
}
AddProjectArguments(scriptPath, scriptArgs, context);
});

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");
}

resourceBuilder.PublishAsDockerFile();

return resourceBuilder;
}

private static void AddProjectArguments(string scriptPath, string[] scriptArgs, CommandLineArgsCallbackContext context)
{
context.Args.Add(scriptPath);

foreach (var arg in scriptArgs)
{
context.Args.Add(arg);
}
}

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");
}
}
3 changes: 2 additions & 1 deletion src/Aspire.Hosting.Python/PythonProjectResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
namespace Aspire.Hosting.Python;

/// <summary>
/// A resource that represents a Python project.
/// This method is retained only for compatibility.
/// </summary>
/// <param name="name">The name of the resource.</param>
/// <param name="executablePath">The path to the executable used to run the python project.</param>
/// <param name="projectDirectory">The path to the directory containing the python project.</param>
[Obsolete("PythonProjectResource is deprecated. Please use PythonAppResource instead.")]
public class PythonProjectResource(string name, string executablePath, string projectDirectory)

Check failure on line 17 in src/Aspire.Hosting.Python/PythonProjectResource.cs

View check run for this annotation

Azure Pipelines / dotnet.aspire (Build Linux)

src/Aspire.Hosting.Python/PythonProjectResource.cs#L17

src/Aspire.Hosting.Python/PythonProjectResource.cs(17,14): error CS0101: (NETCORE_ENGINEERING_TELEMETRY=Build) The namespace 'Aspire.Hosting.Python' already contains a definition for 'PythonProjectResource'

Check failure on line 17 in src/Aspire.Hosting.Python/PythonProjectResource.cs

View check run for this annotation

Azure Pipelines / dotnet.aspire (Build Linux)

src/Aspire.Hosting.Python/PythonProjectResource.cs#L17

src/Aspire.Hosting.Python/PythonProjectResource.cs(17,35): error CS8863: (NETCORE_ENGINEERING_TELEMETRY=Build) Only a single partial type declaration may have a parameter list

Check failure on line 17 in src/Aspire.Hosting.Python/PythonProjectResource.cs

View check run for this annotation

Azure Pipelines / dotnet.aspire (Build Linux_helix_tests)

src/Aspire.Hosting.Python/PythonProjectResource.cs#L17

src/Aspire.Hosting.Python/PythonProjectResource.cs(17,14): error CS0101: (NETCORE_ENGINEERING_TELEMETRY=Build) The namespace 'Aspire.Hosting.Python' already contains a definition for 'PythonProjectResource'

Check failure on line 17 in src/Aspire.Hosting.Python/PythonProjectResource.cs

View check run for this annotation

Azure Pipelines / dotnet.aspire (Build Linux_helix_tests)

src/Aspire.Hosting.Python/PythonProjectResource.cs#L17

src/Aspire.Hosting.Python/PythonProjectResource.cs(17,35): error CS8863: (NETCORE_ENGINEERING_TELEMETRY=Build) Only a single partial type declaration may have a parameter list

Check failure on line 17 in src/Aspire.Hosting.Python/PythonProjectResource.cs

View check run for this annotation

Azure Pipelines / dotnet.aspire

src/Aspire.Hosting.Python/PythonProjectResource.cs#L17

src/Aspire.Hosting.Python/PythonProjectResource.cs(17,14): error CS0101: (NETCORE_ENGINEERING_TELEMETRY=Build) The namespace 'Aspire.Hosting.Python' already contains a definition for 'PythonProjectResource'

Check failure on line 17 in src/Aspire.Hosting.Python/PythonProjectResource.cs

View check run for this annotation

Azure Pipelines / dotnet.aspire

src/Aspire.Hosting.Python/PythonProjectResource.cs#L17

src/Aspire.Hosting.Python/PythonProjectResource.cs(17,35): error CS8863: (NETCORE_ENGINEERING_TELEMETRY=Build) Only a single partial type declaration may have a parameter list
: ExecutableResource(ThrowIfNull(name), ThrowIfNull(executablePath), ThrowIfNull(projectDirectory)), IResourceWithServiceDiscovery
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
namespace Aspire.Hosting;

/// <summary>
/// Provides extension methods for adding Python applications to an <see cref="IDistributedApplicationBuilder"/>.
/// This class is retained only for compatibility.
/// </summary>
[Obsolete("PythonProjectResource is deprecated. Please use PythonAppResource instead.")]
public static class PythonProjectResourceBuilderExtensions
{
/// <summary>
/// Adds a python application with a virtual environment to the application model.
/// This method is retained only for compatibility.
/// </summary>
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/> to add the resource to.</param>
/// <param name="name">The name of the resource.</param>
Expand Down Expand Up @@ -57,6 +58,7 @@ public static class PythonProjectResourceBuilderExtensions
/// builder.Build().Run();
/// </code>
/// </example>
[Obsolete("AddPythonProject is deprecated. Please use AddPythonApp instead.")]
public static IResourceBuilder<PythonProjectResource> AddPythonProject(
this IDistributedApplicationBuilder builder, string name, string projectDirectory, string scriptPath, params string[] scriptArgs)
{
Expand All @@ -66,7 +68,7 @@ public static IResourceBuilder<PythonProjectResource> AddPythonProject(
}

/// <summary>
/// Adds a python application with a virtual environment to the application model.
/// This method is retained only for compatibility.
/// </summary>
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/> to add the resource to.</param>
/// <param name="name">The name of the resource.</param>
Expand Down Expand Up @@ -106,6 +108,7 @@ public static IResourceBuilder<PythonProjectResource> AddPythonProject(
/// builder.Build().Run();
/// </code>
/// </example>
[Obsolete("AddPythonProject is deprecated. Please use AddPythonApp instead.")]
public static IResourceBuilder<PythonProjectResource> AddPythonProject(
this IDistributedApplicationBuilder builder, string name, string projectDirectory, string scriptPath,
string virtualEnvironmentPath, params string[] scriptArgs)
Expand Down
4 changes: 2 additions & 2 deletions src/Aspire.Hosting.Python/VirtualEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
namespace Aspire.Hosting.Python;

/// <summary>
/// Handles location of files within the virtual environment of a python project.
/// Handles location of files within the virtual environment of a python app.
/// </summary>
/// <param name="virtualEnvironmentPath">The path to the directory containing the python project files.</param>
/// <param name="virtualEnvironmentPath">The path to the directory containing the python app files.</param>
internal sealed class VirtualEnvironment(string virtualEnvironmentPath)
{
/// <summary>
Expand Down
Loading

0 comments on commit ae6dad1

Please sign in to comment.