Skip to content

Commit b3dd731

Browse files
committed
feat(cli,templates): dev-first templates; stage in-repo lib for pn run
1 parent 2805e1d commit b3dd731

File tree

2 files changed

+40
-9
lines changed
  • src/pythonnative/cli
  • templates/android_template/app/src/main/java/com/pythonnative/android_template

2 files changed

+40
-9
lines changed

src/pythonnative/cli/pn.py

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,20 @@ def _extract_bundled_template(zip_name: str, destination: str) -> None:
104104
Extract a bundled template zip into the destination directory.
105105
Tries package resources first; falls back to repo root `templates/` at dev time.
106106
"""
107-
# Try to load from installed package resources first (if templates are packaged inside the module)
107+
# Dev-first: prefer repository templates if running from a checkout (avoid stale packaged zips)
108+
try:
109+
# __file__ -> src/pythonnative/cli/pn.py, so go up to src/, then to repo root
110+
src_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
111+
repo_root = os.path.abspath(os.path.join(src_dir, ".."))
112+
repo_templates = os.path.join(repo_root, "templates")
113+
candidate = os.path.join(repo_templates, zip_name)
114+
if os.path.exists(candidate):
115+
_extract_zip_to_destination(candidate, destination)
116+
return
117+
except Exception:
118+
pass
119+
120+
# Try to load from installed package resources next (if templates are packaged inside the module)
108121
try:
109122
cand = resources.files("pythonnative").joinpath("templates").joinpath(zip_name)
110123
with resources.as_file(cand) as p:
@@ -139,14 +152,6 @@ def _extract_bundled_template(zip_name: str, destination: str) -> None:
139152
except Exception:
140153
pass
141154

142-
# Fallback: use repository-level templates directory
143-
repo_templates = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "..", "..", "templates")
144-
repo_templates = os.path.abspath(repo_templates)
145-
candidate = os.path.join(repo_templates, zip_name)
146-
if os.path.exists(candidate):
147-
_extract_zip_to_destination(candidate, destination)
148-
return
149-
150155
raise FileNotFoundError(f"Could not find bundled template {zip_name}. Ensure templates are packaged.")
151156

152157

@@ -206,6 +211,25 @@ def run_project(args: argparse.Namespace) -> None:
206211
os.makedirs(dest_dir, exist_ok=True)
207212
shutil.copytree(src_dir, dest_dir, dirs_exist_ok=True)
208213

214+
# During local development (running from repository), also bundle the
215+
# local library sources so the app uses the in-repo version instead of
216+
# the PyPI package. This provides faster inner-loop iteration and avoids
217+
# version skew during development.
218+
try:
219+
# __file__ -> src/pythonnative/cli/pn.py, so repo root is one up from src/
220+
src_root = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), ".."))
221+
local_lib = os.path.join(src_root, "pythonnative")
222+
if os.path.isdir(local_lib):
223+
if platform == "android":
224+
python_root = os.path.join(build_dir, "android_template", "app", "src", "main", "python")
225+
else:
226+
python_root = os.path.join(build_dir) # staged at build/ios/app for iOS below
227+
os.makedirs(python_root, exist_ok=True)
228+
shutil.copytree(local_lib, os.path.join(python_root, "pythonnative"), dirs_exist_ok=True)
229+
except Exception:
230+
# Non-fatal; fallback to the packaged PyPI dependency if present
231+
pass
232+
209233
# Install any necessary Python packages into the project environment
210234
# Skip installation during prepare-only to avoid network access and speed up scaffolding
211235
if not prepare_only:
@@ -291,6 +315,11 @@ def run_project(args: argparse.Namespace) -> None:
291315
staged_app_src = os.path.join(build_dir, "app")
292316
if os.path.isdir(staged_app_src):
293317
shutil.copytree(staged_app_src, os.path.join(app_path, "app"), dirs_exist_ok=True)
318+
# Also copy local library sources if present for dev flow
319+
src_root = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), ".."))
320+
local_lib = os.path.join(src_root, "pythonnative")
321+
if os.path.isdir(local_lib):
322+
shutil.copytree(local_lib, os.path.join(app_path, "pythonnative"), dirs_exist_ok=True)
294323
except Exception:
295324
# Non-fatal; fallback UI will appear if import fails
296325
pass

templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.pythonnative.android_template
22

33
import androidx.appcompat.app.AppCompatActivity
44
import android.os.Bundle
5+
import android.util.Log
56
import android.widget.TextView
67
import com.chaquo.python.Python
78
import com.chaquo.python.android.AndroidPlatform
@@ -21,6 +22,7 @@ class MainActivity : AppCompatActivity() {
2122
pyModule.callAttr("bootstrap", this)
2223
// Python Page will set the content view via set_root_view
2324
} catch (e: Exception) {
25+
Log.e("PythonNative", "Python bootstrap failed", e)
2426
// Fallback: show a simple native label if Python bootstrap fails
2527
val tv = TextView(this)
2628
tv.text = "Hello from PythonNative (Android template)"

0 commit comments

Comments
 (0)