Skip to content

Project Structure

Default Layout

Nuwa uses a simple flat layout for easy development:

my_project/
├── pyproject.toml           # Python project config
├── nim/                     # Nim source files
│   ├── my_project_lib.nim  # Main entry point (filename = module name)
│   └── helpers.nim          # Additional modules
├── my_project/              # Python package
│   ├── __init__.py          # Package wrapper
│   └── my_project_lib.so    # Compiled extension (generated)
├── tests/                   # Test files
│   └── test_my_project.py   # Pytest tests
├── example.py               # Example/test file
└── README.md

Key Concepts

Entry Point

The entry point filename determines the Python module name. If your entry point is my_project_lib.nim, the compiled extension will be importable as my_project_lib.

Flat Layout

Compiled extensions are placed directly in the package directory. No pip install -e . needed - you can run python example.py and pytest directly after compiling.

Naming Convention

The compiled extension is named {module_name}_lib.so (or .pyd on Windows) to avoid conflicts with the Python package directory.

Multi-File Projects

Use include to add code from other Nim files:

nim/my_project_lib.nim:

import nuwa_sdk  # Provides nuwa_export for automatic type stub generation
include helpers  # Include helpers.nim

proc greet(name: string): string {.nuwa_export.} =
  return make_greeting(name)

nim/helpers.nim:

proc make_greeting(name: string): string =
  return "Hello, " & name & "!"

Important

Use include (not import) when building shared libraries. The include directive includes the code at compile time, while import creates a separate module namespace.

Exporting Functions

You must add the {.nuwa_export.} pragma to export functions to Python:

# ✅ Exported - accessible from Python
proc add(a: int, b: int): int {.nuwa_export.} =
  return a + b

# ❌ Not exported - not accessible from Python
proc subtract(a: int, b: int): int =
  return a - b

The {.nuwa_export.} pragma: 1. Makes the function callable from Python (via nimpy) 2. Generates type stub information for IDE autocomplete

Mixing Python and Nim

Your __init__.py can import from the compiled extension and add Python wrappers:

# my_project/__init__.py
from .my_project_lib import *

__version__ = "0.1.0"

# Wrap Nim functions with Python code
def validate_dataframe(df, column_name):
    """Extract pandas data and pass to Nim"""
    import numpy as np
    from ctypes import c_void_p

    # Zero-copy numpy view
    data = df[column_name].to_numpy()

    # Pass pointer to Nim
    result = validate_array_raw(
        data.ctypes.data_as(c_void_p),
        len(data)
    )
    return result

This allows you to:

  • Use Python to extract/prepare data
  • Pass pointers/arrays to Nim for zero-copy processing
  • Return results back to Python