Skip to content

Commit cf45203

Browse files
committed
build(templates,cli): ship template dirs with package; drop zip artifacts
1 parent 4a97378 commit cf45203

File tree

57 files changed

+43
-52
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+43
-52
lines changed

pyproject.toml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,10 @@ where = ["src"]
6666
[tool.setuptools]
6767
license-files = ["LICENSE*"]
6868

69-
# Ensure template zip files are bundled with the wheel so the CLI can run offline.
70-
[tool.setuptools.data-files]
71-
"pythonnative/templates" = [
72-
"templates/android_template.zip",
73-
"templates/ios_template.zip",
69+
# Include template directories inside the package so importlib.resources can find them
70+
[tool.setuptools.package-data]
71+
pythonnative = [
72+
"templates/**",
7473
]
7574

7675
[tool.ruff]

src/pythonnative/cli/pn.py

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import sys
88
import sysconfig
99
import urllib.request
10-
import zipfile
1110
from importlib import resources
1211
from typing import Any, Dict, List, Optional
1312

@@ -97,65 +96,66 @@ def bootstrap(native_instance):
9796
print("Initialized PythonNative project.")
9897

9998

100-
def _extract_zip_to_destination(zip_path: str, destination: str) -> None:
101-
with zipfile.ZipFile(zip_path, "r") as zf:
102-
zf.extractall(destination)
99+
def _copy_dir(src: str, dst: str) -> None:
100+
os.makedirs(os.path.dirname(dst), exist_ok=True)
101+
shutil.copytree(src, dst, dirs_exist_ok=True)
103102

104103

105-
def _extract_bundled_template(zip_name: str, destination: str) -> None:
104+
def _copy_bundled_template_dir(template_dir: str, destination: str) -> None:
106105
"""
107-
Extract a bundled template zip into the destination directory.
108-
Tries package resources first; falls back to repo root `templates/` at dev time.
106+
Copy a bundled template directory into the destination directory.
107+
Tries the repository `templates/` first during development, then
108+
package resources when installed from a wheel.
109+
The result should be `${destination}/{template_dir}`.
109110
"""
110-
# Dev-first: prefer repository templates if running from a checkout (avoid stale packaged zips)
111+
dest_path = os.path.join(destination, template_dir)
112+
113+
# Dev-first: prefer local source templates if running from a checkout (avoid stale packaged data)
111114
try:
112115
# __file__ -> src/pythonnative/cli/pn.py, so go up to src/, then to repo root
113116
src_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
117+
# Check templates located inside the source package tree
118+
local_pkg_templates = os.path.join(src_dir, "pythonnative", "templates", template_dir)
119+
if os.path.isdir(local_pkg_templates):
120+
_copy_dir(local_pkg_templates, dest_path)
121+
return
114122
repo_root = os.path.abspath(os.path.join(src_dir, ".."))
115123
repo_templates = os.path.join(repo_root, "templates")
116-
candidate = os.path.join(repo_templates, zip_name)
117-
if os.path.exists(candidate):
118-
_extract_zip_to_destination(candidate, destination)
124+
candidate_dir = os.path.join(repo_templates, template_dir)
125+
if os.path.isdir(candidate_dir):
126+
_copy_dir(candidate_dir, dest_path)
119127
return
120128
except Exception:
121129
pass
122130

123-
# Try to load from installed package resources next (if templates are packaged inside the module)
131+
# Try to load from installed package resources (templates packaged inside the module)
124132
try:
125-
cand = resources.files("pythonnative").joinpath("templates").joinpath(zip_name)
133+
cand = resources.files("pythonnative").joinpath("templates").joinpath(template_dir)
126134
with resources.as_file(cand) as p:
127135
resource_path = str(p)
128-
if os.path.exists(resource_path):
129-
_extract_zip_to_destination(resource_path, destination)
130-
return
131-
except Exception:
132-
# Not packaged inside the module; try data-files installation locations next
133-
pass
134-
135-
# Try sysconfig data dir (where data-files are typically installed)
136-
try:
137-
data_dir = sysconfig.get_paths().get("data")
138-
if data_dir:
139-
candidate = os.path.join(data_dir, "pythonnative", "templates", zip_name)
140-
if os.path.exists(candidate):
141-
_extract_zip_to_destination(candidate, destination)
136+
if os.path.isdir(resource_path):
137+
_copy_dir(resource_path, dest_path)
142138
return
143139
except Exception:
144140
pass
145141

146-
# Try site-packages purelib/platlib (some environments place data files here)
142+
# Last resort: check typical data-file locations
147143
try:
148-
purelib = sysconfig.get_paths().get("purelib")
149-
platlib = sysconfig.get_paths().get("platlib")
150-
for base in filter(None, [purelib, platlib]):
151-
candidate = os.path.join(base, "pythonnative", "templates", zip_name)
152-
if os.path.exists(candidate):
153-
_extract_zip_to_destination(candidate, destination)
144+
data_paths = sysconfig.get_paths()
145+
search_bases = [
146+
data_paths.get("data"),
147+
data_paths.get("purelib"),
148+
data_paths.get("platlib"),
149+
]
150+
for base in filter(None, search_bases):
151+
candidate_dir = os.path.join(base, "pythonnative", "templates", template_dir)
152+
if os.path.isdir(candidate_dir):
153+
_copy_dir(candidate_dir, dest_path)
154154
return
155155
except Exception:
156156
pass
157157

158-
raise FileNotFoundError(f"Could not find bundled template {zip_name}. Ensure templates are packaged.")
158+
raise FileNotFoundError(f"Could not find bundled template directory {template_dir}. Ensure templates are packaged.")
159159

160160

161161
def _github_json(url: str) -> Any:
@@ -199,8 +199,8 @@ def create_android_project(project_name: str, destination: str) -> None:
199199
:param project_name: The name of the project.
200200
:param destination: The directory where the project will be created.
201201
"""
202-
# Extract the Android template project from bundled zip
203-
_extract_bundled_template("android_template.zip", destination)
202+
# Copy the Android template project directory
203+
_copy_bundled_template_dir("android_template", destination)
204204

205205

206206
def create_ios_project(project_name: str, destination: str) -> None:
@@ -210,8 +210,8 @@ def create_ios_project(project_name: str, destination: str) -> None:
210210
:param project_name: The name of the project.
211211
:param destination: The directory where the project will be created.
212212
"""
213-
# Extract the iOS template project from bundled zip
214-
_extract_bundled_template("ios_template.zip", destination)
213+
# Copy the iOS template project directory
214+
_copy_bundled_template_dir("ios_template", destination)
215215

216216

217217
def run_project(args: argparse.Namespace) -> None:
File renamed without changes.
File renamed without changes.
File renamed without changes.

templates/android_template/app/proguard-rules.pro renamed to src/pythonnative/templates/android_template/app/proguard-rules.pro

File renamed without changes.

templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt renamed to src/pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt

File renamed without changes.

templates/android_template/app/src/main/AndroidManifest.xml renamed to src/pythonnative/templates/android_template/app/src/main/AndroidManifest.xml

File renamed without changes.

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

File renamed without changes.

templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml renamed to src/pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml

File renamed without changes.

0 commit comments

Comments
 (0)