python2mobile 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. examples/example_ecommerce_app.py +189 -0
  2. examples/example_todo_app.py +159 -0
  3. p2m/__init__.py +31 -0
  4. p2m/cli.py +470 -0
  5. p2m/config.py +205 -0
  6. p2m/core/__init__.py +18 -0
  7. p2m/core/api.py +191 -0
  8. p2m/core/ast_walker.py +171 -0
  9. p2m/core/database.py +192 -0
  10. p2m/core/events.py +56 -0
  11. p2m/core/render_engine.py +597 -0
  12. p2m/core/runtime.py +128 -0
  13. p2m/core/state.py +51 -0
  14. p2m/core/validator.py +284 -0
  15. p2m/devserver/__init__.py +9 -0
  16. p2m/devserver/server.py +84 -0
  17. p2m/i18n/__init__.py +7 -0
  18. p2m/i18n/translator.py +74 -0
  19. p2m/imagine/__init__.py +35 -0
  20. p2m/imagine/agent.py +463 -0
  21. p2m/imagine/legacy.py +217 -0
  22. p2m/llm/__init__.py +20 -0
  23. p2m/llm/anthropic_provider.py +78 -0
  24. p2m/llm/base.py +42 -0
  25. p2m/llm/compatible_provider.py +120 -0
  26. p2m/llm/factory.py +72 -0
  27. p2m/llm/ollama_provider.py +89 -0
  28. p2m/llm/openai_provider.py +79 -0
  29. p2m/testing/__init__.py +41 -0
  30. p2m/ui/__init__.py +43 -0
  31. p2m/ui/components.py +301 -0
  32. python2mobile-1.0.1.dist-info/METADATA +238 -0
  33. python2mobile-1.0.1.dist-info/RECORD +50 -0
  34. python2mobile-1.0.1.dist-info/WHEEL +5 -0
  35. python2mobile-1.0.1.dist-info/entry_points.txt +2 -0
  36. python2mobile-1.0.1.dist-info/top_level.txt +3 -0
  37. tests/test_basic_engine.py +281 -0
  38. tests/test_build_generation.py +603 -0
  39. tests/test_build_test_gate.py +150 -0
  40. tests/test_carousel_modal.py +84 -0
  41. tests/test_config_system.py +272 -0
  42. tests/test_i18n.py +101 -0
  43. tests/test_ifood_app_integration.py +172 -0
  44. tests/test_imagine_cli.py +133 -0
  45. tests/test_imagine_command.py +341 -0
  46. tests/test_llm_providers.py +321 -0
  47. tests/test_new_apps_integration.py +588 -0
  48. tests/test_ollama_functional.py +329 -0
  49. tests/test_real_world_apps.py +228 -0
  50. tests/test_run_integration.py +776 -0
@@ -0,0 +1,189 @@
1
+ """
2
+ Example: E-commerce App - Real-world P2M application
3
+ """
4
+
5
+ from p2m.core import Render
6
+ from p2m.ui import Container, Text, Button, Input, Image, Row, Column, Card, Badge
7
+
8
+
9
+ # Mock product data
10
+ products = [
11
+ {
12
+ "id": 1,
13
+ "name": "Wireless Headphones",
14
+ "price": 79.99,
15
+ "rating": 4.5,
16
+ "image": "https://via.placeholder.com/200x200?text=Headphones",
17
+ "in_stock": True,
18
+ },
19
+ {
20
+ "id": 2,
21
+ "name": "Smart Watch",
22
+ "price": 199.99,
23
+ "rating": 4.8,
24
+ "image": "https://via.placeholder.com/200x200?text=SmartWatch",
25
+ "in_stock": True,
26
+ },
27
+ {
28
+ "id": 3,
29
+ "name": "USB-C Cable",
30
+ "price": 12.99,
31
+ "rating": 4.2,
32
+ "image": "https://via.placeholder.com/200x200?text=Cable",
33
+ "in_stock": False,
34
+ },
35
+ ]
36
+
37
+ # Shopping cart
38
+ cart = []
39
+
40
+
41
+ def add_to_cart(product_id: int):
42
+ """Add product to cart"""
43
+ product = next((p for p in products if p["id"] == product_id), None)
44
+ if product:
45
+ cart.append(product)
46
+ print(f"Added {product['name']} to cart")
47
+
48
+
49
+ def remove_from_cart(product_id: int):
50
+ """Remove product from cart"""
51
+ global cart
52
+ cart = [p for p in cart if p["id"] != product_id]
53
+ print(f"Removed product {product_id} from cart")
54
+
55
+
56
+ def render_product_card(product: dict):
57
+ """Render a product card"""
58
+ card = Card(class_="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow")
59
+
60
+ # Product image
61
+ image = Image(
62
+ src=product["image"],
63
+ alt=product["name"],
64
+ class_="w-full h-48 object-cover"
65
+ )
66
+ card.add(image)
67
+
68
+ # Product info
69
+ info = Container(class_="p-4")
70
+
71
+ # Name
72
+ name = Text(product["name"], class_="text-lg font-semibold text-gray-800 mb-2")
73
+ info.add(name)
74
+
75
+ # Rating and price row
76
+ rating_price = Row(class_="flex justify-between items-center mb-3")
77
+
78
+ rating = Badge(
79
+ label=f"⭐ {product['rating']}",
80
+ class_="bg-yellow-100 text-yellow-800 px-2 py-1 rounded text-sm"
81
+ )
82
+ rating_price.add(rating)
83
+
84
+ price = Text(f"${product['price']}", class_="text-xl font-bold text-blue-600")
85
+ rating_price.add(price)
86
+
87
+ info.add(rating_price)
88
+
89
+ # Stock status
90
+ stock_class = "bg-green-100 text-green-800" if product["in_stock"] else "bg-red-100 text-red-800"
91
+ stock_text = "In Stock" if product["in_stock"] else "Out of Stock"
92
+ stock = Badge(
93
+ label=stock_text,
94
+ class_=f"{stock_class} px-2 py-1 rounded text-xs font-semibold"
95
+ )
96
+ info.add(stock)
97
+
98
+ # Add to cart button
99
+ btn_class = "bg-blue-600 hover:bg-blue-700 text-white" if product["in_stock"] else "bg-gray-400 text-gray-600 cursor-not-allowed"
100
+ add_btn = Button(
101
+ "Add to Cart",
102
+ class_=f"{btn_class} w-full mt-4 px-4 py-2 rounded font-semibold",
103
+ on_click=lambda: add_to_cart(product["id"]) if product["in_stock"] else None
104
+ )
105
+ info.add(add_btn)
106
+
107
+ card.add(info)
108
+ return card
109
+
110
+
111
+ def create_view():
112
+ """Create the e-commerce app view"""
113
+
114
+ # Main container
115
+ main = Container(class_="bg-gray-50 min-h-screen")
116
+
117
+ # Header
118
+ header = Container(class_="bg-blue-600 text-white p-4 shadow-md")
119
+
120
+ title = Text("TechStore", class_="text-2xl font-bold mb-2")
121
+ header.add(title)
122
+
123
+ subtitle = Text("Your favorite tech products", class_="text-blue-100")
124
+ header.add(subtitle)
125
+
126
+ main.add(header)
127
+
128
+ # Search bar
129
+ search_section = Container(class_="bg-white p-4 border-b border-gray-200")
130
+ search_input = Input(
131
+ placeholder="Search products...",
132
+ class_="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
133
+ )
134
+ search_section.add(search_input)
135
+ main.add(search_section)
136
+
137
+ # Products section
138
+ products_section = Container(class_="p-4")
139
+
140
+ section_title = Text("Featured Products", class_="text-xl font-bold text-gray-800 mb-4")
141
+ products_section.add(section_title)
142
+
143
+ # Products grid
144
+ products_grid = Container(class_="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4")
145
+
146
+ for product in products:
147
+ product_card = render_product_card(product)
148
+ products_grid.add(product_card)
149
+
150
+ products_section.add(products_grid)
151
+ main.add(products_section)
152
+
153
+ # Cart summary
154
+ cart_section = Container(class_="bg-white p-4 border-t border-gray-200 sticky bottom-0")
155
+
156
+ cart_row = Row(class_="flex justify-between items-center")
157
+
158
+ cart_info = Column()
159
+ cart_label = Text("Shopping Cart", class_="font-semibold text-gray-800")
160
+ cart_info.add(cart_label)
161
+
162
+ cart_count = Text(
163
+ f"{len(cart)} items • ${sum(p['price'] for p in cart):.2f}",
164
+ class_="text-sm text-gray-600"
165
+ )
166
+ cart_info.add(cart_count)
167
+
168
+ cart_row.add(cart_info)
169
+
170
+ checkout_btn = Button(
171
+ "Checkout",
172
+ class_="bg-green-600 hover:bg-green-700 text-white px-6 py-2 rounded font-semibold",
173
+ on_click=lambda: print("Checkout clicked")
174
+ )
175
+ cart_row.add(checkout_btn)
176
+
177
+ cart_section.add(cart_row)
178
+ main.add(cart_section)
179
+
180
+ return main.build()
181
+
182
+
183
+ def main():
184
+ """Main entry point"""
185
+ Render.execute(create_view)
186
+
187
+
188
+ if __name__ == "__main__":
189
+ main()
@@ -0,0 +1,159 @@
1
+ """
2
+ Example: Todo App - Real-world P2M application
3
+ """
4
+
5
+ from p2m.core import Render
6
+ from p2m.ui import Container, Text, Button, Input, List, Row, Column, Card, Badge
7
+
8
+
9
+ # App state (in real app, would be managed by a state manager)
10
+ todos = [
11
+ {"id": 1, "title": "Learn Python2Mobile", "completed": False},
12
+ {"id": 2, "title": "Build a mobile app", "completed": False},
13
+ {"id": 3, "title": "Deploy to production", "completed": False},
14
+ ]
15
+
16
+
17
+ def add_todo(title: str):
18
+ """Add a new todo"""
19
+ new_id = max([t["id"] for t in todos]) + 1 if todos else 1
20
+ todos.append({"id": new_id, "title": title, "completed": False})
21
+ print(f"Added todo: {title}")
22
+
23
+
24
+ def toggle_todo(todo_id: int):
25
+ """Toggle todo completion status"""
26
+ for todo in todos:
27
+ if todo["id"] == todo_id:
28
+ todo["completed"] = not todo["completed"]
29
+ print(f"Toggled todo {todo_id}")
30
+
31
+
32
+ def delete_todo(todo_id: int):
33
+ """Delete a todo"""
34
+ global todos
35
+ todos = [t for t in todos if t["id"] != todo_id]
36
+ print(f"Deleted todo {todo_id}")
37
+
38
+
39
+ def render_todo_item(todo: dict):
40
+ """Render a single todo item"""
41
+ row = Row(class_="bg-white p-4 rounded-lg shadow-sm border border-gray-200 flex items-center justify-between")
42
+
43
+ # Left side: checkbox and title
44
+ left = Column(class_="flex-1")
45
+
46
+ status_badge = Badge(
47
+ label="✓" if todo["completed"] else "○",
48
+ class_="bg-blue-100 text-blue-800 px-2 py-1 rounded text-sm font-semibold"
49
+ )
50
+ left.add(status_badge)
51
+
52
+ title_class = "text-gray-500 line-through" if todo["completed"] else "text-gray-800"
53
+ title = Text(todo["title"], class_=f"{title_class} text-lg font-medium")
54
+ left.add(title)
55
+
56
+ row.add(left)
57
+
58
+ # Right side: action buttons
59
+ right = Row(class_="space-x-2")
60
+
61
+ toggle_btn = Button(
62
+ "Toggle",
63
+ class_="bg-green-500 hover:bg-green-600 text-white px-3 py-1 rounded text-sm",
64
+ on_click=lambda: toggle_todo(todo["id"])
65
+ )
66
+ right.add(toggle_btn)
67
+
68
+ delete_btn = Button(
69
+ "Delete",
70
+ class_="bg-red-500 hover:bg-red-600 text-white px-3 py-1 rounded text-sm",
71
+ on_click=lambda: delete_todo(todo["id"])
72
+ )
73
+ right.add(delete_btn)
74
+
75
+ row.add(right)
76
+ return row
77
+
78
+
79
+ def create_view():
80
+ """Create the todo app view"""
81
+
82
+ # Main container
83
+ main = Container(class_="bg-gray-50 min-h-screen p-4")
84
+
85
+ # Header
86
+ header = Container(class_="mb-6")
87
+ title = Text("My Todo App", class_="text-3xl font-bold text-gray-800 mb-2")
88
+ header.add(title)
89
+
90
+ subtitle = Text(
91
+ f"You have {len([t for t in todos if not t['completed']])} active tasks",
92
+ class_="text-gray-600"
93
+ )
94
+ header.add(subtitle)
95
+ main.add(header)
96
+
97
+ # Add todo section
98
+ add_section = Card(class_="bg-white p-6 rounded-lg shadow-md mb-6")
99
+
100
+ add_title = Text("Add New Todo", class_="text-lg font-semibold text-gray-800 mb-4")
101
+ add_section.add(add_title)
102
+
103
+ input_row = Row(class_="space-x-2")
104
+ input_field = Input(
105
+ placeholder="Enter a new todo...",
106
+ class_="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
107
+ )
108
+ input_row.add(input_field)
109
+
110
+ add_btn = Button(
111
+ "Add",
112
+ class_="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-lg font-semibold",
113
+ on_click=lambda: add_todo(input_field.props.get("value", ""))
114
+ )
115
+ input_row.add(add_btn)
116
+
117
+ add_section.add(input_row)
118
+ main.add(add_section)
119
+
120
+ # Todo list section
121
+ list_section = Card(class_="bg-white p-6 rounded-lg shadow-md")
122
+
123
+ list_title = Text("Todo List", class_="text-lg font-semibold text-gray-800 mb-4")
124
+ list_section.add(list_title)
125
+
126
+ if not todos:
127
+ empty_msg = Text(
128
+ "No todos yet. Add one to get started!",
129
+ class_="text-gray-500 text-center py-8"
130
+ )
131
+ list_section.add(empty_msg)
132
+ else:
133
+ # Render each todo
134
+ for todo in todos:
135
+ todo_item = render_todo_item(todo)
136
+ list_section.add(todo_item)
137
+
138
+ main.add(list_section)
139
+
140
+ # Footer stats
141
+ footer = Container(class_="mt-6 text-center text-gray-600 text-sm")
142
+ completed_count = len([t for t in todos if t["completed"]])
143
+ stats_text = Text(
144
+ f"{completed_count}/{len(todos)} tasks completed",
145
+ class_="text-gray-600"
146
+ )
147
+ footer.add(stats_text)
148
+ main.add(footer)
149
+
150
+ return main.build()
151
+
152
+
153
+ def main():
154
+ """Main entry point"""
155
+ Render.execute(create_view)
156
+
157
+
158
+ if __name__ == "__main__":
159
+ main()
p2m/__init__.py ADDED
@@ -0,0 +1,31 @@
1
+ """
2
+ Python2Mobile - Write mobile apps in pure Python
3
+ """
4
+
5
+ __version__ = "0.1.0"
6
+ __author__ = "P2M Team"
7
+ __license__ = "MIT"
8
+
9
+ from p2m.core import Render
10
+ from p2m.ui import (
11
+ Container,
12
+ Text,
13
+ Button,
14
+ Input,
15
+ Image,
16
+ List,
17
+ Navigator,
18
+ Screen,
19
+ )
20
+
21
+ __all__ = [
22
+ "Render",
23
+ "Container",
24
+ "Text",
25
+ "Button",
26
+ "Input",
27
+ "Image",
28
+ "List",
29
+ "Navigator",
30
+ "Screen",
31
+ ]