{"library":"pyobjc-framework-metal","title":"PyObjC Metal Framework","description":"Wrappers for the “Metal” framework on macOS. PyObjC allows full-featured Cocoa applications to be written in pure Python, bridging Python and Objective-C. This specific package provides bindings for Apple's Metal framework, enabling GPU-accelerated computing and graphics. It is actively maintained with frequent updates tied to macOS SDK releases, currently at version 12.1.","language":"python","status":"active","last_verified":"Sat May 16","install":{"commands":["pip install pyobjc-framework-metal"],"cli":null},"imports":["import Metal"],"auth":{"required":false,"env_vars":[]},"quickstart":{"code":"import Metal\nimport objc\nimport struct\n\ndef run_metal_kernel():\n    # 1. Get the default Metal device\n    device = Metal.MTLCreateSystemDefaultDevice()\n    if device is None:\n        print(\"Error: No Metal device found.\")\n        return\n\n    print(f\"Using Metal device: {device.name()}\")\n\n    # 2. Create a simple Metal shader (MSL) source\n    # This kernel adds two numbers\n    kernel_source = \"\"\"\n        #include <metal_stdlib>\n\n        kernel void add_numbers(\n            device const float *inA [[buffer(0)]],\n            device const float *inB [[buffer(1)]],\n            device float *out [[buffer(2)]],\n            uint id [[thread_position_in_grid]])\n        {\n            out[id] = inA[id] + inB[id];\n        }\n    \"\"\"\n\n    # 3. Create a library from the source\n    # Using newLibraryWithSource_options_error_ instead of newLibraryWithSource_options_error\n    # as PyObjC usually appends '_' to methods with Objective-C error pointers.\n    error_ptr = objc.nil\n    library = device.newLibraryWithSource_options_error_(kernel_source, objc.nil, error_ptr)\n\n    if library is None:\n        # Check if error_ptr now points to an actual error object\n        if error_ptr and error_ptr[0] is not objc.nil: # error_ptr is a C array of MTL_Error* in PyObjC\n            error_obj = error_ptr[0]\n            print(f\"Failed to create Metal library: {error_obj.localizedDescription()}\")\n        else:\n            print(\"Failed to create Metal library (unknown error).\")\n        return\n\n    # 4. Get the kernel function\n    function = library.newFunctionWithName_(\"add_numbers\")\n    if function is None:\n        print(\"Error: Failed to find kernel function 'add_numbers'.\")\n        return\n\n    # 5. Create a compute pipeline state\n    pipeline_state = device.newComputePipelineStateWithFunction_error_(function, objc.nil)\n    if pipeline_state is None:\n        print(\"Error: Failed to create compute pipeline state.\")\n        return\n\n    # 6. Prepare data\n    data_size = 10 * struct.calcsize('f') # 10 floats\n    input_a = [float(i) for i in range(10)]\n    input_b = [float(i * 2) for i in range(10)]\n    output_data = [0.0] * 10\n\n    # Create Metal buffers\n    buffer_a = device.newBufferWithBytes_length_options_(bytes(struct.pack('f'*10, *input_a)), data_size, Metal.MTLResourceStorageModeManaged)\n    buffer_b = device.newBufferWithBytes_length_options_(bytes(struct.pack('f'*10, *input_b)), data_size, Metal.MTLResourceStorageModeManaged)\n    buffer_out = device.newBufferWithLength_options_(data_size, Metal.MTLResourceStorageModeManaged)\n\n    # 7. Create a command queue\n    command_queue = device.newCommandQueue_()\n    if command_queue is None:\n        print(\"Error: Failed to create command queue.\")\n        return\n\n    # 8. Create a command buffer\n    command_buffer = command_queue.commandBuffer_()\n\n    # 9. Create a compute command encoder\n    compute_encoder = command_buffer.computeCommandEncoder_()\n    compute_encoder.setComputePipelineState_(pipeline_state)\n    compute_encoder.setBuffer_offset_atIndex_(buffer_a, 0, 0)\n    compute_encoder.setBuffer_offset_atIndex_(buffer_b, 0, 1)\n    compute_encoder.setBuffer_offset_atIndex_(buffer_out, 0, 2)\n\n    # 10. Dispatch threads\n    grid_size = Metal.MTLSizeMake(10, 1, 1)\n    thread_group_size = Metal.MTLSizeMake(min(10, pipeline_state.maxTotalThreadsPerThreadgroup()), 1, 1)\n    compute_encoder.dispatchThreads_threadsPerThreadgroup_(grid_size, thread_group_size)\n\n    compute_encoder.endEncoding_()\n\n    # 11. Commit and wait for completion\n    command_buffer.commit_()\n    command_buffer.waitUntilCompleted_()\n\n    # 12. Read results back to CPU (if using managed storage mode)\n    buffer_out.didModifyRange_(Metal.NSMakeRange(0, data_size))\n    result_bytes = buffer_out.contents().tobytes()\n    result = struct.unpack('f'*10, result_bytes)\n\n    print(\"Input A:\", input_a)\n    print(\"Input B:\", input_b)\n    print(\"Output (A+B):\", list(result))\n\n    expected_output = [input_a[i] + input_b[i] for i in range(10)]\n    if all(abs(r - e) < 1e-5 for r, e in zip(result, expected_output)):\n        print(\"✅ Output matches expected values.\")\n    else:\n        print(\"❌ Output does not match expected values.\")\n\nif __name__ == '__main__':\n    run_metal_kernel()","lang":"python","description":"This quickstart demonstrates how to execute a basic Metal compute kernel using `pyobjc-framework-metal`. It initializes a Metal device, compiles a simple shader to add two arrays of numbers, sets up input/output buffers, dispatches the compute command, and reads the results back.","tag":null,"tag_description":null,"last_tested":null,"results":[]},"compatibility":{"tag":null,"tag_description":null,"last_tested":"2026-05-16","installed_version":null,"pypi_latest":"12.1","is_stale":null,"summary":{"python_range":"3.10–3.9","success_rate":0,"avg_install_s":null,"avg_import_s":null,"wheel_type":null},"results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"pyobjc-framework-metal","exit_code":1,"wheel_type":null,"failure_reason":"build_error","import_side_effects":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"pyobjc-framework-metal","exit_code":1,"wheel_type":null,"failure_reason":"build_error","import_side_effects":null,"install_time_s":2.9,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"pyobjc-framework-metal","exit_code":1,"wheel_type":null,"failure_reason":"build_error","import_side_effects":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"pyobjc-framework-metal","exit_code":1,"wheel_type":null,"failure_reason":"build_error","import_side_effects":null,"install_time_s":3,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"pyobjc-framework-metal","exit_code":1,"wheel_type":null,"failure_reason":"build_error","import_side_effects":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"pyobjc-framework-metal","exit_code":1,"wheel_type":null,"failure_reason":"build_error","import_side_effects":null,"install_time_s":3,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"pyobjc-framework-metal","exit_code":1,"wheel_type":null,"failure_reason":"build_error","import_side_effects":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"pyobjc-framework-metal","exit_code":1,"wheel_type":null,"failure_reason":"build_error","import_side_effects":null,"install_time_s":2.6,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"pyobjc-framework-metal","exit_code":1,"wheel_type":null,"failure_reason":"build_error","import_side_effects":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"pyobjc-framework-metal","exit_code":1,"wheel_type":null,"failure_reason":"build_error","import_side_effects":null,"install_time_s":3.5,"import_time_s":null,"mem_mb":null,"disk_size":null}]}}