Skip to content

Custom Attributes

This guide demonstrates how to create custom attributes to extend Belay.NET's functionality, including the powerful new PythonCodeAttribute for embedding Python code directly in method declarations.

PythonCodeAttribute - Direct Python Code Embedding

The PythonCodeAttribute allows you to embed Python code directly within C# method declarations with automatic parameter substitution:

csharp
using Belay.Attributes;

public interface ILedController
{
    [Task]
    [PythonCode(@"
        import machine
        led = machine.Pin({pin}, machine.Pin.OUT)
        led.value({state})
        print(f'LED on pin {pin} set to {state}')
    ")]
    Task SetLEDAsync(int pin, bool state);

    [Task]
    [PythonCode(@"
        import machine
        sensor = machine.ADC(machine.Pin({pin}))
        reading = sensor.read_u16()
        temperature = (reading * 3.3 / 65535) * 100
        temperature
    ")]
    Task<float> ReadTemperatureAsync(int pin);
}

Parameter Substitution

The attribute automatically substitutes method parameters using {paramName} syntax:

csharp
[PythonCode(@"
    result = calculate_complex_value({x}, {y}, '{operation}')
    result
")]
Task<float> CalculateAsync(float x, float y, string operation);

Method Interception Usage

Use with method interception for seamless C# to Python execution:

csharp
using var device = Device.FromConnectionString("serial:COM3");
await device.ConnectAsync();

// Create proxy - automatically converts method calls to Python execution
var controller = device.CreateProxy<ILedController>();

// These method calls execute the embedded Python code on the device
await controller.SetLEDAsync(25, true);    // Turn on built-in LED
var temp = await controller.ReadTemperatureAsync(26); // Read from ADC pin

Advanced Custom Attribute Patterns

This section covers additional patterns for extending Belay.NET's functionality:

  • Custom Attribute Creation: Extending the base attribute classes
  • Direct Execution Integration: Using attributes with direct Python execution
  • Policy Implementation: Implementing custom retry, timeout, and caching policies
  • Method Interception: Advanced method interception patterns
  • Performance Optimization: Custom attributes for performance-critical scenarios
  • Domain-Specific Attributes: Creating attributes for specific hardware or use cases
  • Validation Attributes: Input validation and parameter checking
  • Logging Attributes: Custom logging and telemetry collection

Custom Attribute Implementation

csharp
// Example custom caching attribute
[AttributeUsage(AttributeTargets.Method)]
public class CacheResultAttribute : TaskAttribute
{
    public int CacheDurationMs { get; set; } = 30000;
    public string? CacheKey { get; set; }
    
    public CacheResultAttribute(int cacheDurationMs = 30000)
    {
        CacheDurationMs = cacheDurationMs;
    }
}

// Usage with simplified Device API
public interface ISensorInterface
{
    [Task]
    [CacheResult(CacheDurationMs = 60000, CacheKey = "device_info")]
    [PythonCode("sys.version")]
    Task<string> GetDeviceInfoAsync();
        
    [Task]
    [CacheResult(10000)] // Cache for 10 seconds
    [PythonCode(@"
        import machine
        sensor = machine.ADC(machine.Pin(26))
        reading = sensor.read_u16()
        temperature = (reading * 3.3 / 65535) * 100
        temperature
    ")]
    Task<float> ReadTemperatureAsync();
}

// Direct usage with Device
using var device = Device.FromConnectionString("serial:COM3");
await device.ConnectAsync();

var sensor = device.CreateProxy<ISensorInterface>();
var temp = await sensor.ReadTemperatureAsync(); // Cached result

Need help now? Check our GitHub Discussions or review the attribute programming guide.

Released under the Apache License 2.0.