pyproject-appimage 3.1__tar.gz → 4.1__tar.gz

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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: pyproject-appimage
3
- Version: 3.1
3
+ Version: 4.1
4
4
  Summary: Generate AppImages from your Python projects
5
5
  Author-email: JakobDev <jakobdev@gmx.de>
6
6
  License: BSD-2-Clause
@@ -25,6 +25,7 @@ Requires-Python: >=3.9
25
25
  Description-Content-Type: text/markdown
26
26
  License-File: LICENSE
27
27
  Requires-Dist: requests
28
+ Requires-Dist: desktop-entry-lib
28
29
  Requires-Dist: tomli; python_version < "3.11"
29
30
 
30
31
  # pyproject-appimage
@@ -66,6 +67,7 @@ The following options can be used in your pyproject.toml:
66
67
  | python-version | string | The Python version that is used. Default is your current version. Can be overwritten with the cli. |
67
68
  | updateinformation | string | The [update information](https://github.com/AppImage/AppImageSpec/blob/master/draft.md#update-information)
68
69
  | compression | string | The Squashfs compression
70
+ | additional-packages | list of strins | A list of packages that should also be installed
69
71
 
70
72
  Note: All paths are relativ to your project directory
71
73
 
@@ -92,7 +94,12 @@ options:
92
94
  ```
93
95
 
94
96
  ## Projects using pyproject-appimage
97
+ * [jdMinecraftLauncher](https://codeberg.org/JakobDev/jdMinecraftLauncher)
95
98
  * [jdNBTExplorer](https://codeberg.org/JakobDev/jdNBTExplorer)
99
+ * [jdReplace](https://codeberg.org/JakobDev/jdReplace)
100
+ * [jdMrpackInstaller](https://codeberg.org/JakobDev/jdMrpackInstaller)
101
+ * [jdDesktopEntryEdit](https://codeberg.org/JakobDev/jdDesktopEntryEdit)
102
+ * [jdAppStreamEdit](https://codeberg.org/JakobDev/jdAppStreamEdit)
96
103
 
97
104
  [pyproject-appimage is of course also available as AppImage](https://codeberg.org/JakobDev/pyproject-appimage/releases/latest)
98
105
 
@@ -37,6 +37,7 @@ The following options can be used in your pyproject.toml:
37
37
  | python-version | string | The Python version that is used. Default is your current version. Can be overwritten with the cli. |
38
38
  | updateinformation | string | The [update information](https://github.com/AppImage/AppImageSpec/blob/master/draft.md#update-information)
39
39
  | compression | string | The Squashfs compression
40
+ | additional-packages | list of strins | A list of packages that should also be installed
40
41
 
41
42
  Note: All paths are relativ to your project directory
42
43
 
@@ -63,7 +64,12 @@ options:
63
64
  ```
64
65
 
65
66
  ## Projects using pyproject-appimage
67
+ * [jdMinecraftLauncher](https://codeberg.org/JakobDev/jdMinecraftLauncher)
66
68
  * [jdNBTExplorer](https://codeberg.org/JakobDev/jdNBTExplorer)
69
+ * [jdReplace](https://codeberg.org/JakobDev/jdReplace)
70
+ * [jdMrpackInstaller](https://codeberg.org/JakobDev/jdMrpackInstaller)
71
+ * [jdDesktopEntryEdit](https://codeberg.org/JakobDev/jdDesktopEntryEdit)
72
+ * [jdAppStreamEdit](https://codeberg.org/JakobDev/jdAppStreamEdit)
67
73
 
68
74
  [pyproject-appimage is of course also available as AppImage](https://codeberg.org/JakobDev/pyproject-appimage/releases/latest)
69
75
 
@@ -28,26 +28,30 @@ classifiers = [
28
28
  ]
29
29
  dependencies = [
30
30
  "requests",
31
+ "desktop-entry-lib",
31
32
  "tomli; python_version < '3.11'"
32
33
  ]
33
34
  dynamic = ["version"]
34
35
 
35
- [tool.setuptools.dynamic]
36
- version = { file = "pyproject_appimage/version.txt" }
37
-
38
- [project.scripts]
39
- pyproject-appimage = "pyproject_appimage:main"
40
-
41
- [tool.setuptools.package-data]
42
- pyproject_appimage = ["version.txt", "default.png", "default.desktop"]
43
-
44
36
  [project.urls]
45
37
  Downloads = "https://codeberg.org/JakobDev/pyproject-appimage/releases"
46
38
  Issues = "https://codeberg.org/JakobDev/pyproject-appimage/issues"
47
39
  Source = "https://codeberg.org/JakobDev/pyproject-appimage"
48
40
  Donation = "https://ko-fi.com/jakobdev"
49
41
 
42
+ [tool.setuptools.package-dir]
43
+ pyproject_appimage = "pyproject_appimage"
44
+
45
+ [tool.setuptools.dynamic]
46
+ version = { file = "pyproject_appimage/version.txt" }
47
+
48
+ [tool.setuptools.package-data]
49
+ pyproject_appimage = ["version.txt", "default.png"]
50
+
51
+ [project.scripts]
52
+ pyproject-appimage = "pyproject_appimage:main"
53
+
50
54
  [tool.pyproject-appimage]
51
55
  script = "pyproject-appimage"
52
56
  output = "pyproject-appimage.AppImage"
53
- python-version ="3.11"
57
+ python-version ="3.12"
@@ -1,4 +1,5 @@
1
1
  from typing import Optional, Any, TypedDict
2
+ import desktop_entry_lib
2
3
  import subprocess
3
4
  import argparse
4
5
  import requests
@@ -34,6 +35,18 @@ PyprojectDict = TypedDict("PyprojectDict", {
34
35
  }, total=False)
35
36
 
36
37
 
38
+ def read_pyproject_file(path: str) -> dict[str, Any]:
39
+ with open(path, "rb") as f:
40
+ try:
41
+ return toml_load(f)
42
+ except Exception as ex:
43
+ if len(ex.args) == 1:
44
+ print("Error while parsing " + os.path.join(args.project_dir, "pyproject.toml") + f": {ex.args[0]}", file=sys.stderr)
45
+ else:
46
+ print("Error while parsing " + os.path.join(args.project_dir, "pyproject.toml"), file=sys.stderr)
47
+ sys.exit(1)
48
+
49
+
37
50
  def check_key(project_dir: str, pyproject: PyprojectDict, key: str, error_list: list[str], checks: list[str]) -> None:
38
51
  if key not in pyproject:
39
52
  if "required" in checks:
@@ -50,6 +63,13 @@ def check_key(project_dir: str, pyproject: PyprojectDict, key: str, error_list:
50
63
  if "bool" in checks:
51
64
  if not isinstance(pyproject[key], bool):
52
65
  error_list.append(f"\"{key}\" must be a bool")
66
+ if "string-list" in checks:
67
+ if not isinstance(pyproject[key], list):
68
+ error_list.append(f"\"{key}\" must be a list of strings")
69
+ else:
70
+ for i in pyproject[key]:
71
+ if not isinstance(i, str):
72
+ error_list.append(f"\"{key}\" must be a list of strings")
53
73
 
54
74
 
55
75
  def check_pyproject(project_dir: str, pyproject: PyprojectDict) -> None:
@@ -69,6 +89,7 @@ def check_pyproject(project_dir: str, pyproject: PyprojectDict) -> None:
69
89
  check_key(project_dir, pyproject, "output", error_list, ["string"])
70
90
  check_key(project_dir, pyproject, "updateinformation", error_list, ["string"])
71
91
  check_key(project_dir, pyproject, "compression", error_list, ["string"])
92
+ check_key(project_dir, pyproject, "additional-packages", error_list, ["string-list"])
72
93
 
73
94
  if ("gettext-desktop-entry" in pyproject or "gettext-appstream" in pyproject) and not "gettext-directory" in pyproject:
74
95
  error_list.append("\"gettext-directory\" must be set when using gettext")
@@ -122,6 +143,84 @@ def get_icon_size(work_dir: str, icon_path: str) -> tuple[int, int]:
122
143
  return (int(width), int(height))
123
144
 
124
145
 
146
+ def handle_icon(project_dir: str, work_dir: str, app_root: str, pyproject: PyprojectDict) -> None:
147
+ if "icon" not in pyproject:
148
+ shutil.copyfile(os.path.join(os.path.dirname(__file__), "default.png"), os.path.join(app_root, ".DirIcon"))
149
+ shutil.copyfile(os.path.join(os.path.dirname(__file__), "default.png"), os.path.join(app_root, "pyproject-appimage-default.png"))
150
+ return
151
+
152
+ icon_path = os.path.join(project_dir, pyproject["icon"])
153
+
154
+ if icon_path.endswith(".png"):
155
+ shutil.copyfile(icon_path, os.path.join(app_root, ".DirIcon"))
156
+ else:
157
+ # If a Icon is not PNG, convert it
158
+ if icon_path.endswith(".svg"):
159
+ # https://stackoverflow.com/a/55370062
160
+ subprocess.run([get_image_magick_command(work_dir), "-background", "none", icon_path, "PNG:" + os.path.join(app_root, ".DirIcon")], check=True)
161
+ else:
162
+ subprocess.run([get_image_magick_command(work_dir), icon_path, "PNG:" + os.path.join(app_root, ".DirIcon")], check=True)
163
+
164
+ if "rename-icon" in pyproject:
165
+ icon_name = pyproject["rename-icon"]
166
+ else:
167
+ icon_name = os.path.basename(pyproject["icon"])
168
+
169
+ shutil.copyfile(icon_path, os.path.join(app_root, icon_name))
170
+
171
+ if icon_name.endswith(".svg"):
172
+ icon_dir = os.path.join(app_root, "usr", "share", "icons", "hicolor", "scalable", "apps")
173
+ else:
174
+ icon_size = get_icon_size(work_dir, os.path.join(app_root, icon_name))
175
+ icon_dir = os.path.join(app_root, "usr", "share", "icons", "hicolor", f"{icon_size[0]}x{icon_size[1]}", "apps")
176
+
177
+ if not os.path.isdir(icon_dir):
178
+ os.makedirs(icon_dir)
179
+
180
+ shutil.copyfile(os.path.join(app_root, icon_name), os.path.join(icon_dir, icon_name))
181
+
182
+
183
+ def create_desktop_entry(project_dir: str, app_root: str, pyproject: PyprojectDict) -> None:
184
+ full_pyproject = read_pyproject_file(os.path.join(project_dir, "pyproject.toml"))
185
+
186
+ entry = desktop_entry_lib.DesktopEntry()
187
+
188
+ if "project" in full_pyproject:
189
+ entry.Name.default_text = full_pyproject["project"].get("name", "App")
190
+ if "description" in full_pyproject["project"]:
191
+ entry.Comment.default_text = full_pyproject["project"]["description"]
192
+ else:
193
+ entry.Name.default_text = "App"
194
+
195
+ entry.Icon = "pyproject-appimage-default"
196
+ entry.Exec = pyproject["script"]
197
+
198
+ entry.Categories.append("Utility")
199
+
200
+ entry.write_file(os.path.join(app_root, "pyproject-appimage-default.desktop"))
201
+
202
+
203
+ def handle_desktop_entry(project_dir: str, app_root: str, pyproject: PyprojectDict) -> None:
204
+ if "desktop-entry" not in pyproject:
205
+ create_desktop_entry(project_dir, app_root, pyproject)
206
+ return
207
+
208
+ desktop_source_path = os.path.join(project_dir, pyproject["desktop-entry"])
209
+ desktop_dest_path = os.path.join(app_root, pyproject.get("rename-desktop-entry", os.path.basename(pyproject["desktop-entry"])))
210
+
211
+ if pyproject.get("gettext-desktop-entry", False):
212
+ subprocess.run(["msgfmt", "--desktop", "--template", desktop_source_path, "-d", os.path.join(project_dir, pyproject["gettext-directory"]), "-o", desktop_dest_path], check=True)
213
+ else:
214
+ shutil.copyfile(desktop_source_path, desktop_dest_path)
215
+
216
+ desktop_share_dir = os.path.join(app_root, "usr", "share", "applications")
217
+
218
+ if not os.path.isdir(desktop_share_dir):
219
+ os.makedirs(desktop_share_dir)
220
+
221
+ shutil.copyfile(desktop_dest_path, os.path.join(desktop_share_dir, os.path.basename(desktop_dest_path)))
222
+
223
+
125
224
  def build_appimage(project_dir: str, work_dir: str, pyproject: PyprojectDict, args: argparse.Namespace) -> None:
126
225
  if args.python_version is not None:
127
226
  python_version = args.python_version
@@ -141,7 +240,7 @@ def build_appimage(project_dir: str, work_dir: str, pyproject: PyprojectDict, ar
141
240
  if args.appimagekit_url:
142
241
  download_file(args.appimagekit_url, os.path.join(work_dir, "Appimagetool.AppImage"))
143
242
  else:
144
- download_file("https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage", os.path.join(work_dir, "Appimagetool.AppImage"))
243
+ download_file("https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage", os.path.join(work_dir, "Appimagetool.AppImage"))
145
244
 
146
245
  subprocess.run(["chmod", "+x", os.path.join(work_dir, "Python.AppImage")], check=True)
147
246
  subprocess.run(["chmod", "+x", os.path.join(work_dir, "Appimagetool.AppImage")], check=True)
@@ -164,52 +263,9 @@ def build_appimage(project_dir: str, work_dir: str, pyproject: PyprojectDict, ar
164
263
 
165
264
  os.symlink(os.path.join("usr", "bin", pyproject["script"]), os.path.join(app_root, "AppRun"))
166
265
 
167
- if "icon" in pyproject:
168
- if pyproject["icon"].endswith(".png"):
169
- shutil.copyfile(os.path.join(project_dir, pyproject["icon"]), os.path.join(app_root, os.path.basename(pyproject["icon"])))
170
- else:
171
- # If a Icon is not PNG, convert it
172
- subprocess.run([get_image_magick_command(work_dir), os.path.join(project_dir, pyproject["icon"]), "PNG:" + os.path.join(app_root, os.path.basename(pyproject["icon"]))], check=True)
266
+ handle_icon(project_dir, work_dir, app_root, pyproject)
173
267
 
174
- if "rename-icon" in pyproject:
175
- os.rename(os.path.join(app_root, os.path.basename(pyproject["icon"])), os.path.join(app_root, pyproject["rename-icon"]))
176
- icon_name = pyproject["rename-icon"]
177
- else:
178
- icon_name = os.path.basename(pyproject["icon"])
179
-
180
- shutil.copyfile(os.path.join(app_root, icon_name), os.path.join(app_root, ".DirIcon"))
181
-
182
- if icon_name.endswith(".svg"):
183
- icon_dir = os.path.join(app_root, "usr", "share", "icons", "hicolor", "scalable", "apps")
184
- else:
185
- icon_size = get_icon_size(work_dir, os.path.join(app_root, icon_name))
186
- icon_dir = os.path.join(app_root, "usr", "share", "icons", "hicolor", f"{icon_size[0]}x{icon_size[1]}", "apps")
187
-
188
- if not os.path.isdir(icon_dir):
189
- os.makedirs(icon_dir)
190
-
191
- shutil.copyfile(os.path.join(app_root, icon_name), os.path.join(icon_dir, icon_name))
192
- else:
193
- shutil.copyfile(os.path.join(os.path.dirname(__file__), "default.png"), os.path.join(app_root, ".DirIcon"))
194
-
195
- if "desktop-entry" in pyproject:
196
- desktop_source_path = os.path.join(project_dir, pyproject["desktop-entry"])
197
- desktop_dest_path = os.path.join(app_root, pyproject.get("rename-desktop-entry", os.path.basename(pyproject["desktop-entry"])))
198
-
199
- if pyproject.get("gettext-desktop-entry", False):
200
- subprocess.run(["msgfmt", "--desktop", "--template", desktop_source_path, "-d", os.path.join(project_dir, pyproject["gettext-directory"]), "-o", desktop_dest_path], check=True)
201
- else:
202
- shutil.copyfile(desktop_source_path, desktop_dest_path)
203
-
204
- desktop_share_dir = os.path.join(app_root, "usr", "share", "applications")
205
-
206
- if not os.path.isdir(desktop_share_dir):
207
- os.makedirs(desktop_share_dir)
208
-
209
- shutil.copyfile(desktop_dest_path, os.path.join(desktop_share_dir, os.path.basename(desktop_dest_path)))
210
- else:
211
- shutil.copyfile(os.path.join(os.path.dirname(__file__), "default.desktop"), os.path.join(app_root, "App.desktop"))
212
- os.symlink(".DirIcon", os.path.join(app_root, "Icon.png"))
268
+ handle_desktop_entry(project_dir, app_root, pyproject)
213
269
 
214
270
  if "appstream" in pyproject:
215
271
  appstream_path = os.path.join(app_root, "usr", "share", "metainfo")
@@ -229,6 +285,9 @@ def build_appimage(project_dir: str, work_dir: str, pyproject: PyprojectDict, ar
229
285
  else:
230
286
  shutil.copyfile(appstream_source_path, appstream_dest_path)
231
287
 
288
+ if "additional-packages" in pyproject:
289
+ subprocess.run([os.path.join(app_root, "usr", "bin", "pip"), "install"] + pyproject["additional-packages"], check=True)
290
+
232
291
  if args.output is not None:
233
292
  output = args.output
234
293
  elif "output" in pyproject:
@@ -285,7 +344,7 @@ def get_toml_section(data: dict[str, Any], name: str) -> Optional[dict[str, Any]
285
344
  return current_data
286
345
 
287
346
 
288
- def main():
347
+ def main() -> None:
289
348
  parser = argparse.ArgumentParser()
290
349
  parser.add_argument("--output", help="Sets the putput filename")
291
350
  parser.add_argument("--project-dir", help="Sets the project dir", default=os.getcwd())
@@ -311,15 +370,7 @@ def main():
311
370
  print(os.path.join(args.project_dir, "pyproject.toml") + " does not exists", file=sys.stderr)
312
371
  sys.exit(1)
313
372
 
314
- with open(os.path.join(args.project_dir, "pyproject.toml"), "rb") as f:
315
- try:
316
- data = toml_load(f)
317
- except Exception as ex:
318
- if len(ex.args) == 1:
319
- print("Error while parsing " + os.path.join(args.project_dir, "pyproject.toml") + f": {ex.args[0]}", file=sys.stderr)
320
- else:
321
- print("Error while parsing " + os.path.join(args.project_dir, "pyproject.toml"), file=sys.stderr)
322
- sys.exit(1)
373
+ data = read_pyproject_file(os.path.join(args.project_dir, "pyproject.toml"))
323
374
 
324
375
  pyproject = get_toml_section(data, PYPROJECT_SECTION)
325
376
 
@@ -341,7 +392,7 @@ def main():
341
392
  pass
342
393
 
343
394
  try:
344
- build_appimage(args.project_dir, args.work_dir, pyproject, args)
395
+ build_appimage(args.project_dir, os.path.abspath(args.work_dir), pyproject, args)
345
396
  except subprocess.CalledProcessError as ex:
346
397
  print("Error while running " + str(ex.cmd), file=sys.stderr)
347
398
  sys.exit(1)
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: pyproject-appimage
3
- Version: 3.1
3
+ Version: 4.1
4
4
  Summary: Generate AppImages from your Python projects
5
5
  Author-email: JakobDev <jakobdev@gmx.de>
6
6
  License: BSD-2-Clause
@@ -25,6 +25,7 @@ Requires-Python: >=3.9
25
25
  Description-Content-Type: text/markdown
26
26
  License-File: LICENSE
27
27
  Requires-Dist: requests
28
+ Requires-Dist: desktop-entry-lib
28
29
  Requires-Dist: tomli; python_version < "3.11"
29
30
 
30
31
  # pyproject-appimage
@@ -66,6 +67,7 @@ The following options can be used in your pyproject.toml:
66
67
  | python-version | string | The Python version that is used. Default is your current version. Can be overwritten with the cli. |
67
68
  | updateinformation | string | The [update information](https://github.com/AppImage/AppImageSpec/blob/master/draft.md#update-information)
68
69
  | compression | string | The Squashfs compression
70
+ | additional-packages | list of strins | A list of packages that should also be installed
69
71
 
70
72
  Note: All paths are relativ to your project directory
71
73
 
@@ -92,7 +94,12 @@ options:
92
94
  ```
93
95
 
94
96
  ## Projects using pyproject-appimage
97
+ * [jdMinecraftLauncher](https://codeberg.org/JakobDev/jdMinecraftLauncher)
95
98
  * [jdNBTExplorer](https://codeberg.org/JakobDev/jdNBTExplorer)
99
+ * [jdReplace](https://codeberg.org/JakobDev/jdReplace)
100
+ * [jdMrpackInstaller](https://codeberg.org/JakobDev/jdMrpackInstaller)
101
+ * [jdDesktopEntryEdit](https://codeberg.org/JakobDev/jdDesktopEntryEdit)
102
+ * [jdAppStreamEdit](https://codeberg.org/JakobDev/jdAppStreamEdit)
96
103
 
97
104
  [pyproject-appimage is of course also available as AppImage](https://codeberg.org/JakobDev/pyproject-appimage/releases/latest)
98
105
 
@@ -3,7 +3,6 @@ README.md
3
3
  pyproject.toml
4
4
  pyproject_appimage/__init__.py
5
5
  pyproject_appimage/__main__.py
6
- pyproject_appimage/default.desktop
7
6
  pyproject_appimage/default.png
8
7
  pyproject_appimage/version.txt
9
8
  pyproject_appimage.egg-info/PKG-INFO
@@ -1,4 +1,5 @@
1
1
  requests
2
+ desktop-entry-lib
2
3
 
3
4
  [:python_version < "3.11"]
4
5
  tomli
@@ -1,6 +0,0 @@
1
- [Desktop Entry]
2
- Name=App
3
- Type=Application
4
- Exec=AppRun
5
- Icon=Icon
6
- Categories=Development;