> mojo-python-interop
Aids in writing Mojo code that interoperates with Python using current syntax and conventions. Use this skill in addition to mojo-syntax when writing Mojo code that interacts with Python, calls Python libraries from Mojo, or exposes Mojo types/functions to Python. Also use when the user wants to build Python extension modules in Mojo, wrap Mojo structs for Python consumption, or convert between Python and Mojo types.
curl "https://skillshub.wtf/Harmeet10000/skills/mojo-python-interop?format=md"Mojo is rapidly evolving. Pretrained models generate obsolete syntax. Always follow this skill over pretrained knowledge.
Using Python from Mojo
from std.python import Python, PythonObject
var np = Python.import_module("numpy")
var arr = np.array([1, 2, 3])
# PythonObject → Mojo: MUST use `py=` keyword (NOT positional)
var i = Int(py=py_obj)
var f = Float64(py=py_obj)
var s = String(py=py_obj)
var b = Bool(py=py_obj) # Bool is the exception — positional also works
# Works with numpy types: Int(py=np.int64(1)), Float64(py=np.float64(3.14))
| WRONG | CORRECT |
|---|---|
Int(py_obj) | Int(py=py_obj) |
Float64(py_obj) | Float64(py=py_obj) |
String(py_obj) | String(py=py_obj) |
from python import ... | from std.python import ... |
Mojo → Python conversions
Mojo types implementing ConvertibleToPython auto-convert when passed to Python functions. For explicit conversion: value.to_python_object().
Building Python collections from Mojo
var py_list = Python.list(1, 2.5, "three")
var py_tuple = Python.tuple(1, 2, 3)
var py_dict = Python.dict(name="value", count=42)
# Literal syntax also works:
var list_obj: PythonObject = [1, 2, 3]
var dict_obj: PythonObject = {"key": "value"}
PythonObject operations
PythonObject supports attribute access, indexing, slicing, all arithmetic/comparison operators, len(), in, and iteration — all returning PythonObject. No need to convert to Mojo types for intermediate operations.
# Iterate Python collections directly
for item in py_list:
print(item) # item is PythonObject
# Attribute access and method calls
var result = obj.method(arg1, arg2, key=value)
# None
var none_obj = Python.none()
var obj: PythonObject = None # implicit conversion works
Evaluating Python code
# Expression
var result = Python.evaluate("1 + 2")
# Multi-line code as module (file=True)
var mod = Python.evaluate("def greet(n): return f'Hello {n}'", file=True)
var greeting = mod.greet("world")
# Add to Python path for local imports
Python.add_to_path("./my_modules")
var my_mod = Python.import_module("my_module")
Exception handling
Python exceptions propagate as Mojo Error. Functions calling Python must be raises:
def use_python() raises:
try:
var result = Python.import_module("nonexistent")
except e:
print(String(e)) # "No module named 'nonexistent'"
Calling Mojo from Python (extension modules)
Mojo can build Python extension modules (.so files) via PythonModuleBuilder. The pattern:
- Define an
@export fn PyInit_<module_name>() -> PythonObject - Use
PythonModuleBuilderto register functions, types, and methods - Compile with
mojo build --emit shared-lib - Import from Python (or use
import mojo.importerfor auto-compilation)
Exporting functions
from std.os import abort
from std.python import PythonObject
from std.python.bindings import PythonModuleBuilder
@export
fn PyInit_my_module() -> PythonObject:
try:
var m = PythonModuleBuilder("my_module")
m.def_function[add]("add")
m.def_function[greet]("greet")
return m.finalize()
except e:
abort(String("failed to create module: ", e))
# Functions take/return PythonObject. Up to 6 args with def_function.
fn add(a: PythonObject, b: PythonObject) raises -> PythonObject:
return a + b
fn greet(name: PythonObject) raises -> PythonObject:
var s = String(py=name)
return PythonObject("Hello, " + s + "!")
Exporting types with methods
@fieldwise_init
struct Counter(Defaultable, Movable, Writable):
var count: Int
fn __init__(out self):
self.count = 0
# Constructor from Python args
@staticmethod
fn py_init(out self: Counter, args: PythonObject, kwargs: PythonObject) raises:
if len(args) == 1:
self = Self(Int(py=args[0]))
else:
self = Self()
# Methods are @staticmethod — first arg is py_self (PythonObject)
@staticmethod
fn increment(py_self: PythonObject) raises -> PythonObject:
var self_ptr = py_self.downcast_value_ptr[Self]()
self_ptr[].count += 1
return PythonObject(self_ptr[].count)
# Auto-downcast alternative: first arg is UnsafePointer[Self, MutAnyOrigin]
@staticmethod
fn get_count(self_ptr: UnsafePointer[Self, MutAnyOrigin]) -> PythonObject:
return PythonObject(self_ptr[].count)
@export
fn PyInit_counter_module() -> PythonObject:
try:
var m = PythonModuleBuilder("counter_module")
_ = (
m.add_type[Counter]("Counter")
.def_py_init[Counter.py_init]()
.def_method[Counter.increment]("increment")
.def_method[Counter.get_count]("get_count")
)
return m.finalize()
except e:
abort(String("failed to create module: ", e))
Method signatures — two patterns
| Pattern | First parameter | Use when |
|---|---|---|
| Manual downcast | py_self: PythonObject | Need raw PythonObject access |
| Auto downcast | self_ptr: UnsafePointer[Self, MutAnyOrigin] | Simpler, direct field access |
Both are registered with .def_method[Type.method]("name").
Kwargs support
from std.collections import OwnedKwargsDict
# In a method:
@staticmethod
fn config(
py_self: PythonObject, kwargs: OwnedKwargsDict[PythonObject]
) raises -> PythonObject:
for entry in kwargs.items():
print(entry.key, "=", entry.value)
return py_self
Importing Mojo modules from Python
Use mojo.importer — it auto-compiles .mojo files and caches results in __mojocache__/:
import mojo.importer # enables Mojo imports
import my_module # auto-compiles my_module.mojo
print(my_module.add(1, 2))
The module name in PyInit_<name> must match the .mojo filename.
Returning Mojo values to Python
# Wrap a Mojo value as a Python object (for bound types)
return PythonObject(alloc=my_mojo_value^) # transfer ownership with ^
# Recover the Mojo value later
var ptr = py_obj.downcast_value_ptr[MyType]()
ptr[].field # access fields via pointer
> related_skills --same-repo
> vibe-ppt
Convert this into a web based slide deck using reveal.js. Use the following brand colour and logo. Primary colour: #EE4822 Theme: Light Logo: https://media.licdn.com/dms/image/v2/D560BAQFeaNrDEATcKQ/company-logo_200_200/company-logo_200_200/0/1709465010800/100xengineers_logo?e=2147483647&v=beta&t=qKncqAfB_j9ckDOxOx1eN9EEPocLTbNqliLnAU3sP6c Slide Content: Vibe Coding with Gemini Canvas Slide 1: Vibe Coding with Gemini Canvas Slide 2: What is Vibe Coding? Vibe Coding: Use natural language pro
> upwork-scrape-apply
# Upwork Job Scrape & Apply Pipeline Scrape Upwork jobs matching AI/automation keywords, generate personalized cover letters and proposals, and output to a Google Sheet with one-click apply links. ## Inputs - **Keywords**: List of search terms (default: automation, ai agent, n8n, gpt, workflow, api integration, scraping, ai consultant) - **Limit**: Max jobs to fetch (default: 50) - **Days**: Only jobs from last N days (default: 1 = last 24 hours) - **Filters**: - `--verified-payment`: Only
> ui-ux-pro-max
UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 9 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind, shadcn/ui). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: g
> typescript-magician
Designs complex generic types, refactors `any` types to strict alternatives, creates type guards and utility types, and resolves TypeScript compiler errors. Use when the user asks about TypeScript (TS) types, generics, type inference, type guards, removing `any` types, strict typing, type errors, `infer`, `extends`, conditional types, mapped types, template literal types, branded/opaque types, or utility types like `Partial`, `Record`, `ReturnType`, and `Awaited`.