pythonhere 0.3.0__py3-none-any.whl → 0.4.0__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.
@@ -0,0 +1,145 @@
1
+ """Find ML model files managed by PythonHere."""
2
+
3
+ import os
4
+ import sys
5
+
6
+
7
+ def _android_models_directory():
8
+ """Return the model directory belonging to the running Android app."""
9
+
10
+ from jnius import autoclass
11
+
12
+ PythonActivity = autoclass("org.kivy.android.PythonActivity")
13
+ activity = PythonActivity.mActivity
14
+ if activity is None:
15
+ raise RuntimeError("A running PythonHere activity is required")
16
+
17
+ external_files = activity.getExternalFilesDir(None)
18
+ if external_files is None:
19
+ raise RuntimeError("Android external app storage is unavailable")
20
+
21
+ return os.path.join(str(external_files.getAbsolutePath()), "models")
22
+
23
+
24
+ def _is_android():
25
+ return sys.platform == "android" or "ANDROID_ARGUMENT" in os.environ
26
+
27
+
28
+ def models_directory(create=False):
29
+ """Return PythonHere's platform-specific model directory.
30
+
31
+ Android uses the app-specific external files directory. Linux uses
32
+ PythonHere's per-user application data directory.
33
+ """
34
+
35
+ if _is_android():
36
+ path = _android_models_directory()
37
+ else:
38
+ path = os.path.expanduser("~/.local/share/pythonhere/models")
39
+
40
+ path = os.path.abspath(path)
41
+ if create:
42
+ os.makedirs(path, exist_ok=True)
43
+ return path
44
+
45
+
46
+ def _relative_parts(value, label):
47
+ """Return safe POSIX-style path components for a remote identifier."""
48
+
49
+ parts = value.split("/")
50
+ if not value or any(part in {"", ".", ".."} for part in parts):
51
+ raise ValueError(f"{label} must be a relative path without dot components")
52
+ return parts
53
+
54
+
55
+ def model_path(repo_id, filename):
56
+ """Return the local path for a model identified by repository and file."""
57
+
58
+ path = os.path.join(
59
+ models_directory(),
60
+ *_relative_parts(repo_id, "repo_id"),
61
+ *_relative_parts(filename, "filename"),
62
+ )
63
+ return os.path.abspath(path)
64
+
65
+
66
+ def require_model(repo_id, filename):
67
+ """Return an installed model path, or raise ``FileNotFoundError``."""
68
+
69
+ path = model_path(repo_id, filename)
70
+ if not os.path.isfile(path):
71
+ raise FileNotFoundError(
72
+ f"ML model is not installed: {repo_id}/{filename} ({path})"
73
+ )
74
+ return path
75
+
76
+
77
+ def _model_extensions(extensions):
78
+ """Normalize an optional model-extension filter."""
79
+
80
+ if extensions is None:
81
+ return None
82
+ if isinstance(extensions, str):
83
+ extensions = (extensions,)
84
+
85
+ normalized = set()
86
+ for extension in extensions:
87
+ if not isinstance(extension, str) or not extension:
88
+ raise ValueError("extensions must contain non-empty strings")
89
+ normalized_extension = extension.lower()
90
+ normalized.add(
91
+ normalized_extension
92
+ if normalized_extension.startswith(".")
93
+ else f".{normalized_extension}"
94
+ )
95
+ return tuple(sorted(normalized))
96
+
97
+
98
+ def _discover_directory(directory, extensions):
99
+ """Yield model files recursively beneath a directory."""
100
+
101
+ path = os.path.abspath(os.fspath(directory))
102
+ if not os.path.isdir(path):
103
+ return
104
+
105
+ def ignore_error(_error):
106
+ pass
107
+
108
+ for root, directories, files in os.walk(path, onerror=ignore_error):
109
+ directories.sort(key=str.casefold)
110
+ for filename in files:
111
+ lower_filename = filename.lower()
112
+ if lower_filename.endswith((".part", ".part.json")):
113
+ continue
114
+ if extensions is None or lower_filename.endswith(extensions):
115
+ yield os.path.abspath(os.path.join(root, filename))
116
+
117
+
118
+ def discover_models(extra_directories=(), *, extensions=".litertlm"):
119
+ """Find local model files, optionally filtered by filename extension.
120
+
121
+ ``extensions`` may be one extension or an iterable, with or without the
122
+ leading dot. The default finds LiteRT-LM models. Pass ``None`` to return
123
+ all completed files. Temporary files belonging to an in-progress download
124
+ are always ignored.
125
+ """
126
+
127
+ if isinstance(extra_directories, (str, bytes, os.PathLike)):
128
+ extra_directories = (extra_directories,)
129
+ extensions = _model_extensions(extensions)
130
+
131
+ models = []
132
+ seen = set()
133
+ for directory in (models_directory(), *extra_directories):
134
+ for discovered_path in _discover_directory(directory, extensions):
135
+ key = os.path.normcase(discovered_path)
136
+ if key not in seen:
137
+ seen.add(key)
138
+ models.append(discovered_path)
139
+ return sorted(
140
+ models,
141
+ key=lambda path: (os.path.basename(path).casefold(), path.casefold()),
142
+ )
143
+
144
+
145
+ __all__ = ("discover_models", "model_path", "models_directory", "require_model")
@@ -1 +1 @@
1
- __version__ = "0.3.0"
1
+ __version__ = "0.4.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonhere
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Here is the Kivy based app to run code from the Jupyter magic %there
5
5
  Author-email: b3b <ash.b3b@gmail.com>
6
6
  License-Expression: MIT
@@ -16,10 +16,13 @@ Classifier: Programming Language :: Python :: 3.14
16
16
  Requires-Python: >=3.10
17
17
  Description-Content-Type: text/x-rst
18
18
  License-File: LICENSE
19
- Requires-Dist: herethere[magic]>=0.3.1
19
+ Requires-Dist: herethere[magic]>=0.3.2
20
20
  Requires-Dist: ipython
21
21
  Requires-Dist: ipywidgets
22
22
  Requires-Dist: Pillow
23
+ Provides-Extra: litert
24
+ Requires-Dist: litert-lm-api==0.16.0; extra == "litert"
25
+ Requires-Dist: requests; extra == "litert"
23
26
  Dynamic: license-file
24
27
 
25
28
  PythonHere
@@ -10,9 +10,9 @@ pythonhere/patches_here.py,sha256=zB7Ohbk5PIXt0imnnrV5Mtl-Y7jpD_p5IBPlijQcCCQ,17
10
10
  pythonhere/pythonhere.kv,sha256=x1xwiKvJAevqBhtXGLb_axIf-VRos4V_jM9Q2i44f2U,1453
11
11
  pythonhere/server_here.py,sha256=dhNMJj1Abz6RZq6SGqkh8-PVOxKThpI519Msoy5WCgM,1862
12
12
  pythonhere/tools_here.py,sha256=RWPBX309d7E4mjkiszzsoJwdaSwXZIqb7ODcRBljC0Y,11437
13
- pythonhere/version_here.py,sha256=VrXpHDu3erkzwl_WXrqINBm9xWkcyUy53IQOj042dOs,22
13
+ pythonhere/version_here.py,sha256=42STGor_9nKYXumfeV5tiyD_M8VdcddX7CEexmibPBk,22
14
14
  pythonhere/window_here.py,sha256=oFPoNMGafGo4Eij3iU1WwaftmRa05Thm1jzuEAQIwK8,2584
15
- pythonhere/.agents/skills/pythonhere/SKILL.md,sha256=WLYmv0ntjVsK4EOKhVdpylp9Tc6pyeWRuos-oeJg_so,10542
15
+ pythonhere/.agents/skills/pythonhere/SKILL.md,sha256=sZruiB7DaMeFLNAm2YLrPE7emmHELj2SEieMfxGrGJw,11080
16
16
  pythonhere/.agents/skills/pythonhere/agents/openai.yaml,sha256=LuAs_jPFRl9Mhvb8bU8E8JSnCc8uc5JYFqYxlhd5elw,210
17
17
  pythonhere/.agents/skills/pythonhere/references/able.md,sha256=3KFblzcGy8VDmriqgcWtqyfZZXoYfDV7kZyKIESCZ_c,22918
18
18
  pythonhere/.agents/skills/pythonhere/references/android-media.md,sha256=vM55sxJmvkLQmKAjHZYL_z0F0SR__PjOHSXtGIgP1BI,6631
@@ -22,6 +22,7 @@ pythonhere/.agents/skills/pythonhere/references/android-runtime.md,sha256=VCZkyw
22
22
  pythonhere/.agents/skills/pythonhere/references/jnius.md,sha256=lyjdGUsZXw2lS-0eppS2-JNTVgEED30z1DglzT6H0D8,14999
23
23
  pythonhere/.agents/skills/pythonhere/references/kivy-kv.md,sha256=9PQR4j-LT66cOUKYCM6AuHXIs5ywmutOQKfxr3frDYw,8546
24
24
  pythonhere/.agents/skills/pythonhere/references/kivy-runtime.md,sha256=ggPCmea5lkdg4p6LlxIVUstJhxC40SbrysqXXQ7-lmM,14616
25
+ pythonhere/.agents/skills/pythonhere/references/litert.md,sha256=MAchOo-acMlSwkmtSkAizNMdwKJHzIe87gFyzq6hyZE,4238
25
26
  pythonhere/.agents/skills/pythonhere/references/midi.md,sha256=1rB4t7LWnMi139Ou896FzgU-6CGFSNVO1QF9kRBvUgo,10934
26
27
  pythonhere/.agents/skills/pythonhere/references/plyer.md,sha256=FYqumbRo826aLrOiRGckS8Oq26v_PoVrSTVbTBZRBs8,6713
27
28
  pythonhere/data/logo/logo-128.png,sha256=4sitDJcVBHOU5UPxicmrzc6YqFY7_xtYrc_ULyrw9ks,4161
@@ -41,6 +42,9 @@ pythonhere/magic_here/prompts/kivy-kv.md,sha256=ajIDSo6PG1_7gHcgfNrOARQOUlUEPUae
41
42
  pythonhere/magic_here/prompts/kivy-runtime.md,sha256=cAgOF0uE3RKYdKQus7h4lklKtANaLPcOYilGShjNtgM,14621
42
43
  pythonhere/magic_here/prompts/midi.md,sha256=2rKvgrwlmeA4MfdnRH9xnTSN0OFOm4mTxtgdYdMAd3Q,10611
43
44
  pythonhere/magic_here/prompts/plyer.md,sha256=wplCHPXkN1vU_thGIxvALuYPB6QDdjCHFhoc93ppvAE,6708
45
+ pythonhere/ml_here/__init__.py,sha256=TiAtiBcGl5jkTmm3Rbt1mlPWRFo4BQTcgHHAzYOs_i0,346
46
+ pythonhere/ml_here/huggingface.py,sha256=857iOkJh90jbxhwyTo1EJ2enVwql3rI8-wUvK04UM9s,33030
47
+ pythonhere/ml_here/storage.py,sha256=JlKIad1bjVR7OSY2KFkyujyQA0dTH2DQPFJvFGH7AEE,4606
44
48
  pythonhere/ui_here/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
49
  pythonhere/ui_here/actionbar_here.kv,sha256=cOjY4ByOyBDUbeS9kbI3JAhi_X3kJ3Pb5h4RL58urwU,214
46
50
  pythonhere/ui_here/common_here.kv,sha256=vnNyDwAKKNbRKvh0QKMbTvR0jNDKiY8DdGRnoEEexSc,324
@@ -51,8 +55,8 @@ pythonhere/ui_here/server_screen_here.kv,sha256=x0Se8hmgjS57gFIPa1r2lf6yJchs3lXO
51
55
  pythonhere/ui_here/server_screen_here.py,sha256=2zQv1YFmiKNxa__7UZDYbAAAwYFLoZPSZPhr7vjCAkU,925
52
56
  pythonhere/ui_here/settings_here.kv,sha256=stMcDq8dCYjay4idK30_7EUu5edkMDAQVPv0H0GuAcM,710
53
57
  pythonhere/ui_here/settings_here.py,sha256=7NflZBUqY2ANZaSEuubaX9wIKniqbekcNQkUbqhhmto,4093
54
- pythonhere-0.3.0.dist-info/licenses/LICENSE,sha256=nW9_eVi3dSMOkr6xghsobAovBEJiffjl_5WjujJTa74,1071
55
- pythonhere-0.3.0.dist-info/METADATA,sha256=atBipqGUbZ_WsS26XcbpouMep9sQpyQaPaIrz0Y5RNc,5348
56
- pythonhere-0.3.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
57
- pythonhere-0.3.0.dist-info/top_level.txt,sha256=dvkfRGF1tFbkjXzD9vwqXTge1Znkv7ga5fRJy5yhJsE,11
58
- pythonhere-0.3.0.dist-info/RECORD,,
58
+ pythonhere-0.4.0.dist-info/licenses/LICENSE,sha256=nW9_eVi3dSMOkr6xghsobAovBEJiffjl_5WjujJTa74,1071
59
+ pythonhere-0.4.0.dist-info/METADATA,sha256=_Fwxkb2YFoMe4IHOHzOaRLEmWsHjUi8A6JGiLKzN3-4,5470
60
+ pythonhere-0.4.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
61
+ pythonhere-0.4.0.dist-info/top_level.txt,sha256=dvkfRGF1tFbkjXzD9vwqXTge1Znkv7ga5fRJy5yhJsE,11
62
+ pythonhere-0.4.0.dist-info/RECORD,,