Skip to content

Commit f72bde8

Browse files
pi-anlclaude
andcommitted
lib/lvgl: Add Python stubs package structure for IDE support.
Create a proper Python package structure for LVGL type stubs: - Create stubs/pyproject.toml with setuptools-scm versioning - Add stubs/lvgl-stubs/ package directory with __init__.py and py.typed - Update gen_mpy.py to output stubs to package directory by default - Add .gitignore to exclude generated .pyi files but include package structure - Update README.md with comprehensive IDE support documentation The stubs package can be installed with 'pip install -e stubs/' for development or built into a wheel for distribution. Generated stub files include complete type definitions for all LVGL widgets, functions, and enums with extracted documentation. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f9badeb commit f72bde8

File tree

7 files changed

+232
-67
lines changed

7 files changed

+232
-67
lines changed

README.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,3 +507,66 @@ print('\n'.join(dir(lvgl.btn)))
507507
...
508508
```
509509

510+
## IDE Support and Type Stubs
511+
512+
For better development experience with IDEs (VS Code, PyCharm, etc.), this repository includes Python type stubs that provide autocompletion, type checking, and documentation hints.
513+
514+
### Installation
515+
516+
The type stubs are automatically generated during the build process and packaged for easy installation:
517+
518+
#### Development Installation (recommended)
519+
520+
For development with the latest stubs:
521+
522+
```bash
523+
pip install -e /path/to/lv_binding_micropython/stubs
524+
```
525+
526+
#### From Built Package
527+
528+
After building and packaging:
529+
530+
```bash
531+
pip install lvgl-stubs
532+
```
533+
534+
### Features
535+
536+
Once installed, your IDE will automatically provide:
537+
538+
- **Autocompletion** for all LVGL functions, methods, and properties
539+
- **Type checking** with mypy and other type checkers
540+
- **Function signatures** with parameter names and types
541+
- **Documentation strings** extracted from LVGL C headers
542+
- **Source location references** to help navigate LVGL documentation
543+
544+
### Building Stubs
545+
546+
The stubs are automatically generated when running the binding generation:
547+
548+
```bash
549+
# Build with automatic stub generation
550+
make USER_C_MODULES=/path/to/lv_binding_micropython/micropython.cmake
551+
552+
# Or generate stubs manually
553+
cd gen
554+
python gen_mpy.py --stubs /path/to/output/dir [other options...]
555+
```
556+
557+
The generated `lvgl.pyi` stub file contains type definitions for:
558+
- All LVGL widget classes (buttons, labels, sliders, etc.)
559+
- Module-level functions and constants
560+
- Enum definitions with proper typing
561+
- Struct types with documented fields
562+
- Callback function signatures
563+
564+
### Package Structure
565+
566+
The stubs are packaged in `stubs/` directory with:
567+
- `pyproject.toml` - Package configuration with setuptools-scm versioning
568+
- `lvgl-stubs/` - Python package containing type stubs
569+
- `lvgl-stubs/__init__.py` - Package initialization
570+
- `lvgl-stubs/py.typed` - Marks package as typed
571+
- `lvgl-stubs/lvgl.pyi` - Generated type stubs (git-ignored)
572+

gen/gen_mpy.py

Lines changed: 70 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -4445,72 +4445,75 @@ def generate_main_stub(module_name, metadata, doc_index=None):
44454445
with open(args.metadata, "w") as metadata_file:
44464446
json.dump(metadata, metadata_file, indent=4)
44474447

4448-
# Generate Python stub files, if specified
4448+
# Generate Python stub files
44494449

4450-
if args.stubs_dir:
4451-
import os
4452-
4453-
# Create stubs directory if it doesn't exist
4454-
if not os.path.exists(args.stubs_dir):
4455-
os.makedirs(args.stubs_dir)
4456-
4457-
# Prepare metadata for stub generation (reuse metadata structure if it exists)
4458-
if not args.metadata:
4459-
# Generate metadata if not already created
4460-
metadata = collections.OrderedDict()
4461-
metadata["objects"] = {obj_name: obj_metadata[obj_name] for obj_name in obj_names}
4462-
metadata["functions"] = {
4463-
simplify_identifier(f.name): func_metadata[f.name] for f in module_funcs
4464-
}
4465-
metadata["enums"] = {
4466-
get_enum_name(enum_name): obj_metadata[enum_name]
4467-
for enum_name in enums.keys()
4468-
if enum_name not in enum_referenced
4469-
}
4470-
metadata["structs"] = [
4471-
simplify_identifier(struct_name)
4472-
for struct_name in generated_structs
4473-
if struct_name in generated_structs
4474-
]
4475-
metadata["structs"] += [
4476-
simplify_identifier(struct_aliases[struct_name])
4477-
for struct_name in struct_aliases.keys()
4478-
]
4479-
metadata["blobs"] = [
4480-
simplify_identifier(global_name) for global_name in generated_globals
4481-
]
4482-
metadata["int_constants"] = [
4483-
get_enum_name(int_constant) for int_constant in int_constants
4484-
]
4485-
4486-
# Load LVGL source files for documentation extraction
4487-
# Determine LVGL directory - look for it relative to the script location
4450+
# Default to stubs package directory if not specified
4451+
if not args.stubs_dir:
44884452
script_dir = os.path.dirname(os.path.abspath(__file__))
4489-
lvgl_dir = os.path.join(script_dir, "..", "lvgl")
4490-
if not os.path.exists(lvgl_dir):
4491-
# Alternative: try to find it relative to input file
4492-
if args.input:
4493-
input_dir = os.path.dirname(os.path.abspath(args.input[0]))
4494-
lvgl_dir = os.path.join(input_dir, "lvgl")
4495-
4496-
doc_index = None
4497-
try:
4498-
if os.path.exists(lvgl_dir):
4499-
eprint(f"Loading LVGL source files for documentation extraction from: {lvgl_dir}")
4500-
doc_index = load_lvgl_source_files(lvgl_dir)
4501-
eprint(f"Built documentation index with {len(doc_index)} functions")
4502-
else:
4503-
eprint(f"LVGL directory not found at {lvgl_dir}, generating stubs without documentation")
4504-
except Exception as e:
4505-
eprint(f"Warning: Could not load LVGL source files for documentation: {e}")
4506-
4507-
# Generate main module stub
4508-
main_stub_content = generate_main_stub(module_name, metadata, doc_index)
4509-
main_stub_path = os.path.join(args.stubs_dir, f"{module_name}.pyi")
4510-
4511-
with open(main_stub_path, "w") as stub_file:
4512-
stub_file.write(main_stub_content)
4513-
4514-
eprint(f"Generated Python stub file: {main_stub_path}")
4515-
4516-
eprint(f"Generated Python stub file with {len(metadata.get('objects', {}))} widgets, {len(metadata.get('functions', {}))} functions, and {len(metadata.get('enums', {}))} enums")
4453+
args.stubs_dir = os.path.join(script_dir, "..", "stubs", "lvgl-stubs")
4454+
4455+
import os
4456+
4457+
# Create stubs directory if it doesn't exist
4458+
if not os.path.exists(args.stubs_dir):
4459+
os.makedirs(args.stubs_dir)
4460+
4461+
# Prepare metadata for stub generation (reuse metadata structure if it exists)
4462+
if not args.metadata:
4463+
# Generate metadata if not already created
4464+
metadata = collections.OrderedDict()
4465+
metadata["objects"] = {obj_name: obj_metadata[obj_name] for obj_name in obj_names}
4466+
metadata["functions"] = {
4467+
simplify_identifier(f.name): func_metadata[f.name] for f in module_funcs
4468+
}
4469+
metadata["enums"] = {
4470+
get_enum_name(enum_name): obj_metadata[enum_name]
4471+
for enum_name in enums.keys()
4472+
if enum_name not in enum_referenced
4473+
}
4474+
metadata["structs"] = [
4475+
simplify_identifier(struct_name)
4476+
for struct_name in generated_structs
4477+
if struct_name in generated_structs
4478+
]
4479+
metadata["structs"] += [
4480+
simplify_identifier(struct_aliases[struct_name])
4481+
for struct_name in struct_aliases.keys()
4482+
]
4483+
metadata["blobs"] = [
4484+
simplify_identifier(global_name) for global_name in generated_globals
4485+
]
4486+
metadata["int_constants"] = [
4487+
get_enum_name(int_constant) for int_constant in int_constants
4488+
]
4489+
# Load LVGL source files for documentation extraction
4490+
# Determine LVGL directory - look for it relative to the script location
4491+
script_dir = os.path.dirname(os.path.abspath(__file__))
4492+
lvgl_dir = os.path.join(script_dir, "..", "lvgl")
4493+
if not os.path.exists(lvgl_dir):
4494+
# Alternative: try to find it relative to input file
4495+
if args.input:
4496+
input_dir = os.path.dirname(os.path.abspath(args.input[0]))
4497+
lvgl_dir = os.path.join(input_dir, "lvgl")
4498+
4499+
doc_index = None
4500+
try:
4501+
if os.path.exists(lvgl_dir):
4502+
eprint(f"Loading LVGL source files for documentation extraction from: {lvgl_dir}")
4503+
doc_index = load_lvgl_source_files(lvgl_dir)
4504+
eprint(f"Built documentation index with {len(doc_index)} functions")
4505+
else:
4506+
eprint(f"LVGL directory not found at {lvgl_dir}, generating stubs without documentation")
4507+
except Exception as e:
4508+
eprint(f"Warning: Could not load LVGL source files for documentation: {e}")
4509+
4510+
# Generate main module stub
4511+
main_stub_content = generate_main_stub(module_name, metadata, doc_index)
4512+
main_stub_path = os.path.join(args.stubs_dir, f"{module_name}.pyi")
4513+
4514+
with open(main_stub_path, "w") as stub_file:
4515+
stub_file.write(main_stub_content)
4516+
4517+
eprint(f"Generated Python stub file: {main_stub_path}")
4518+
4519+
eprint(f"Generated Python stub file with {len(metadata.get('objects', {}))} widgets, {len(metadata.get('functions', {}))} functions, and {len(metadata.get('enums', {}))} enums")

stubs/.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Generated stub files
2+
lvgl-stubs/*.pyi
3+
4+
# But keep the package structure
5+
!lvgl-stubs/__init__.py
6+
!lvgl-stubs/py.typed
7+
8+
# Version file generated by setuptools-scm
9+
lvgl-stubs/_version.py
10+
11+
# Build artifacts
12+
build/
13+
dist/
14+
*.egg-info/
15+
__pycache__/
16+
*.pyc

stubs/README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# LVGL Type Stubs
2+
3+
This package provides type stubs for LVGL MicroPython bindings, enabling IDE autocompletion, type checking, and better development experience.
4+
5+
## Installation
6+
7+
### Development Installation
8+
9+
For development with the latest stubs:
10+
11+
```bash
12+
pip install -e /path/to/lv_binding_micropython/stubs
13+
```
14+
15+
### From Built Package
16+
17+
After building the stubs:
18+
19+
```bash
20+
pip install lvgl-stubs
21+
```
22+
23+
## Usage
24+
25+
Once installed, your IDE (VS Code, PyCharm, etc.) will automatically use these stubs for:
26+
27+
- Autocompletion
28+
- Type checking with mypy
29+
- Function signatures and documentation
30+
- Parameter hints
31+
32+
## Building
33+
34+
The stubs are automatically generated from the LVGL C headers when running the MicroPython bindings build process. The generated `lvgl.pyi` file will be placed in the `lvgl-stubs/` directory.
35+
36+
## Requirements
37+
38+
- Python 3.8+
39+
- Generated from LVGL C headers with Doxygen-style documentation

stubs/lvgl-stubs/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# LVGL MicroPython Type Stubs
2+
# This package provides type stubs for LVGL MicroPython bindings

stubs/lvgl-stubs/py.typed

Whitespace-only changes.

stubs/pyproject.toml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
[build-system]
2+
requires = ["setuptools>=64", "setuptools-scm>=8"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "lvgl-stubs"
7+
description = "Type stubs for LVGL MicroPython bindings"
8+
readme = "README.md"
9+
license = {text = "MIT"}
10+
authors = [
11+
{name = "LVGL Contributors", email = "lvgl@lvgl.io"},
12+
]
13+
classifiers = [
14+
"Development Status :: 4 - Beta",
15+
"Intended Audience :: Developers",
16+
"License :: OSI Approved :: MIT License",
17+
"Programming Language :: Python :: 3",
18+
"Programming Language :: Python :: 3.8",
19+
"Programming Language :: Python :: 3.9",
20+
"Programming Language :: Python :: 3.10",
21+
"Programming Language :: Python :: 3.11",
22+
"Programming Language :: Python :: 3.12",
23+
"Topic :: Software Development :: Libraries :: Python Modules",
24+
"Typing :: Stubs Only",
25+
]
26+
requires-python = ">=3.8"
27+
dynamic = ["version"]
28+
29+
[project.urls]
30+
Homepage = "https://github.com/lvgl/lv_binding_micropython"
31+
Repository = "https://github.com/lvgl/lv_binding_micropython"
32+
Issues = "https://github.com/lvgl/lv_binding_micropython/issues"
33+
34+
[tool.setuptools-scm]
35+
root = ".."
36+
version_file = "lvgl-stubs/_version.py"
37+
38+
[tool.setuptools.packages.find]
39+
include = ["lvgl-stubs*"]
40+
41+
[tool.setuptools.package-data]
42+
"lvgl-stubs" = ["*.pyi", "py.typed"]

0 commit comments

Comments
 (0)