pythonhere 0.1.5.2__tar.gz → 0.2.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.
Files changed (55) hide show
  1. pythonhere-0.2.1/LICENSE +22 -0
  2. pythonhere-0.2.1/PKG-INFO +128 -0
  3. pythonhere-0.2.1/README.rst +104 -0
  4. pythonhere-0.2.1/pyproject.toml +151 -0
  5. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere/android_here.py +15 -9
  6. pythonhere-0.2.1/pythonhere/data/logo/logo-128.png +0 -0
  7. pythonhere-0.2.1/pythonhere/data/logo/logo-32.png +0 -0
  8. pythonhere-0.2.1/pythonhere/data/logo/logo-splash.png +0 -0
  9. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere/enum_here.py +1 -0
  10. pythonhere-0.2.1/pythonhere/exception_manager_here.kv +22 -0
  11. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere/exception_manager_here.py +14 -30
  12. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere/magic_here/shortcuts.py +6 -5
  13. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere/main.py +90 -36
  14. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere/network_here.py +4 -3
  15. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere/patches_here.py +1 -2
  16. pythonhere-0.2.1/pythonhere/pythonhere.kv +49 -0
  17. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere/server_here.py +5 -5
  18. pythonhere-0.2.1/pythonhere/ui_here/actionbar_here.kv +8 -0
  19. pythonhere-0.2.1/pythonhere/ui_here/common_here.kv +14 -0
  20. pythonhere-0.2.1/pythonhere/ui_here/connection_address_here.kv +32 -0
  21. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere/ui_here/layout_here.py +1 -0
  22. pythonhere-0.2.1/pythonhere/ui_here/server_screen_here.kv +57 -0
  23. pythonhere-0.2.1/pythonhere/ui_here/settings_here.kv +31 -0
  24. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere/ui_here/settings_here.py +3 -3
  25. pythonhere-0.2.1/pythonhere/version_here.py +1 -0
  26. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere/window_here.py +3 -2
  27. pythonhere-0.2.1/pythonhere.egg-info/PKG-INFO +128 -0
  28. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere.egg-info/SOURCES.txt +23 -2
  29. pythonhere-0.2.1/pythonhere.egg-info/requires.txt +4 -0
  30. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/setup.cfg +0 -3
  31. pythonhere-0.2.1/setup.py +3 -0
  32. pythonhere-0.2.1/tests/test_android_here.py +32 -0
  33. pythonhere-0.2.1/tests/test_exception_manager_here.py +20 -0
  34. pythonhere-0.2.1/tests/test_launcher_here.py +50 -0
  35. pythonhere-0.2.1/tests/test_magic.py +73 -0
  36. pythonhere-0.2.1/tests/test_main.py +271 -0
  37. pythonhere-0.2.1/tests/test_network.py +8 -0
  38. pythonhere-0.2.1/tests/test_patches.py +37 -0
  39. pythonhere-0.2.1/tests/test_server_here.py +86 -0
  40. pythonhere-0.2.1/tests/test_settings.py +33 -0
  41. pythonhere-0.2.1/tests/test_window_here.py +14 -0
  42. pythonhere-0.1.5.2/PKG-INFO +0 -119
  43. pythonhere-0.1.5.2/README.rst +0 -101
  44. pythonhere-0.1.5.2/pythonhere/version_here.py +0 -1
  45. pythonhere-0.1.5.2/pythonhere.egg-info/PKG-INFO +0 -119
  46. pythonhere-0.1.5.2/pythonhere.egg-info/requires.txt +0 -22
  47. pythonhere-0.1.5.2/setup.py +0 -63
  48. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere/__init__.py +1 -1
  49. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere/launcher_here.py +3 -3
  50. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere/magic_here/__init__.py +0 -0
  51. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere/ui_here/__init__.py +0 -0
  52. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere/ui_here/connection_address_here.py +2 -2
  53. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere/ui_here/server_screen_here.py +2 -2
  54. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere.egg-info/dependency_links.txt +0 -0
  55. {pythonhere-0.1.5.2 → pythonhere-0.2.1}/pythonhere.egg-info/top_level.txt +0 -0
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 b3b
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,128 @@
1
+ Metadata-Version: 2.4
2
+ Name: pythonhere
3
+ Version: 0.2.1
4
+ Summary: Here is the Kivy based app to run code from the Jupyter magic %there
5
+ Author-email: b3b <ash.b3b@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/b3b/ipython-pythonhere
8
+ Project-URL: Changelog, https://github.com/b3b/pythonhere/blob/master/CHANGELOG.rst
9
+ Keywords: android,ipython,jupyter,magic,kivy
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/x-rst
18
+ License-File: LICENSE
19
+ Requires-Dist: herethere[magic]>=0.2.1
20
+ Requires-Dist: ipython
21
+ Requires-Dist: ipywidgets
22
+ Requires-Dist: Pillow
23
+ Dynamic: license-file
24
+
25
+ PythonHere
26
+ ==========
27
+
28
+ .. start-badges
29
+ .. image:: https://img.shields.io/pypi/status/pythonhere
30
+ :target: https://pypi.python.org/pypi/pythonhere
31
+ :alt: Status
32
+ .. image:: https://img.shields.io/pypi/v/pythonhere.svg
33
+ :target: https://pypi.python.org/pypi/pythonhere
34
+ :alt: Latest version on PyPi
35
+ .. image:: https://img.shields.io/docker/v/herethere/pythonhere?color=%23FFD43B&label=Docker%20Image
36
+ :target: https://hub.docker.com/r/herethere/pythonhere
37
+ :alt: Docker Image Version (latest by date)
38
+ .. image:: https://img.shields.io/pypi/pyversions/pythonhere.svg
39
+ :target: https://pypi.python.org/pypi/pythonhere
40
+ :alt: Supported Python versions
41
+ .. image:: https://github.com/b3b/pythonhere/actions/workflows/tests.yml/badge.svg?branch=master
42
+ :target: https://github.com/b3b/pythonhere/actions/workflows/tests.yml?query=branch%3Amaster
43
+ :alt: CI Status
44
+ .. image:: https://codecov.io/github/b3b/pythonhere/coverage.svg?branch=master
45
+ :target: https://codecov.io/github/b3b/pythonhere?branch=master
46
+ :alt: Code coverage Status
47
+ .. end-badges
48
+
49
+ *PythonHere* lets you run Python code from a local `Jupyter <https://jupyter.org/>`_
50
+ notebook inside a remote `Kivy <https://kivy.org>`_ app.
51
+
52
+ PythonHere has two parts:
53
+
54
+ * *Here* is the remote/server side. It runs a Python environment with a Kivy GUI,
55
+ for example on Android, Raspberry Pi, or another machine.
56
+ * *%there* is the local/client side. It is a Jupyter magic command for running
57
+ code interactively in the remote PythonHere environment.
58
+
59
+ This makes PythonHere useful as a live Python/Kivy playground, and as a way to
60
+ inspect or control a Python app running remotely.
61
+
62
+ Project documentation: https://herethere.me/pythonhere
63
+
64
+ .. image:: https://raw.githubusercontent.com/b3b/pythonhere/master/docs/description.png
65
+ :alt: Project description
66
+
67
+
68
+ Install the Android app
69
+ -----------------------
70
+
71
+ Install *PythonHere* with `Obtainium <https://apps.obtainium.imranr.dev/redirect?r=obtainium://add/https://github.com/b3b/pythonhere>`_
72
+ to receive updates from GitHub Releases.
73
+
74
+ Ready-to-use *PythonHere* APKs are available from the `GitHub Releases <https://github.com/b3b/pythonhere/releases>`_ page.
75
+
76
+ For APK provenance and signing checks, see `Android APK verification <https://github.com/b3b/pythonhere/blob/master/docs/android-apk-verification.rst>`_.
77
+ For a list of Python packages included in the Android build, see `buildozer.spec <https://github.com/b3b/pythonhere/blob/master/buildozer.spec>`_.
78
+
79
+ Start a local Jupyter environment with Docker
80
+ ---------------------------------------------
81
+
82
+ The Docker image is based on `Jupyter Docker Stacks <https://jupyter-docker-stacks.readthedocs.io/en/latest/>`_
83
+ and includes *PythonHere* with usage examples.
84
+
85
+ Example command to start the Docker container::
86
+
87
+ docker run \
88
+ --rm \
89
+ -p 8888:8888 \
90
+ --user root \
91
+ -e CHOWN_EXTRA=/home/jovyan/work \
92
+ -e CHOWN_EXTRA_OPTS='-R' \
93
+ -v "$(pwd)/work":/home/jovyan/work \
94
+ herethere/pythonhere:latest
95
+
96
+ The command exposes the Jupyter server on host port ``8888``. Jupyter logs are
97
+ printed in the terminal and include a URL such as
98
+ ``http://127.0.0.1:8888/?token=...``. Open this URL in a browser to use the
99
+ local Jupyter environment.
100
+
101
+ Files in ``/home/jovyan/work`` inside the container are stored in the local
102
+ ``work`` directory.
103
+
104
+
105
+ Run a local Jupyter environment without Docker
106
+ ----------------------------------------------
107
+
108
+ Commands to run locally::
109
+
110
+ pip install pythonhere jupyter
111
+ jupyter notebook
112
+
113
+
114
+ Build Android app
115
+ -----------------
116
+
117
+ To build with `Buildozer <https://github.com/kivy/buildozer>`_, run in the source directory::
118
+
119
+
120
+ buildozer android debug
121
+
122
+
123
+ Related resources
124
+ -----------------
125
+
126
+ * `Kivy Remote Shell <https://github.com/kivy/kivy-remote-shell>`_ : Remote SSH+Python interactive shell application
127
+ * `herethere <https://github.com/b3b/herethere>`_ : Library for interactive code execution, based on AsyncSSH
128
+ * `AsyncSSH <https://github.com/ronf/asyncssh>`_ : Asynchronous SSH for Python
@@ -0,0 +1,104 @@
1
+ PythonHere
2
+ ==========
3
+
4
+ .. start-badges
5
+ .. image:: https://img.shields.io/pypi/status/pythonhere
6
+ :target: https://pypi.python.org/pypi/pythonhere
7
+ :alt: Status
8
+ .. image:: https://img.shields.io/pypi/v/pythonhere.svg
9
+ :target: https://pypi.python.org/pypi/pythonhere
10
+ :alt: Latest version on PyPi
11
+ .. image:: https://img.shields.io/docker/v/herethere/pythonhere?color=%23FFD43B&label=Docker%20Image
12
+ :target: https://hub.docker.com/r/herethere/pythonhere
13
+ :alt: Docker Image Version (latest by date)
14
+ .. image:: https://img.shields.io/pypi/pyversions/pythonhere.svg
15
+ :target: https://pypi.python.org/pypi/pythonhere
16
+ :alt: Supported Python versions
17
+ .. image:: https://github.com/b3b/pythonhere/actions/workflows/tests.yml/badge.svg?branch=master
18
+ :target: https://github.com/b3b/pythonhere/actions/workflows/tests.yml?query=branch%3Amaster
19
+ :alt: CI Status
20
+ .. image:: https://codecov.io/github/b3b/pythonhere/coverage.svg?branch=master
21
+ :target: https://codecov.io/github/b3b/pythonhere?branch=master
22
+ :alt: Code coverage Status
23
+ .. end-badges
24
+
25
+ *PythonHere* lets you run Python code from a local `Jupyter <https://jupyter.org/>`_
26
+ notebook inside a remote `Kivy <https://kivy.org>`_ app.
27
+
28
+ PythonHere has two parts:
29
+
30
+ * *Here* is the remote/server side. It runs a Python environment with a Kivy GUI,
31
+ for example on Android, Raspberry Pi, or another machine.
32
+ * *%there* is the local/client side. It is a Jupyter magic command for running
33
+ code interactively in the remote PythonHere environment.
34
+
35
+ This makes PythonHere useful as a live Python/Kivy playground, and as a way to
36
+ inspect or control a Python app running remotely.
37
+
38
+ Project documentation: https://herethere.me/pythonhere
39
+
40
+ .. image:: https://raw.githubusercontent.com/b3b/pythonhere/master/docs/description.png
41
+ :alt: Project description
42
+
43
+
44
+ Install the Android app
45
+ -----------------------
46
+
47
+ Install *PythonHere* with `Obtainium <https://apps.obtainium.imranr.dev/redirect?r=obtainium://add/https://github.com/b3b/pythonhere>`_
48
+ to receive updates from GitHub Releases.
49
+
50
+ Ready-to-use *PythonHere* APKs are available from the `GitHub Releases <https://github.com/b3b/pythonhere/releases>`_ page.
51
+
52
+ For APK provenance and signing checks, see `Android APK verification <https://github.com/b3b/pythonhere/blob/master/docs/android-apk-verification.rst>`_.
53
+ For a list of Python packages included in the Android build, see `buildozer.spec <https://github.com/b3b/pythonhere/blob/master/buildozer.spec>`_.
54
+
55
+ Start a local Jupyter environment with Docker
56
+ ---------------------------------------------
57
+
58
+ The Docker image is based on `Jupyter Docker Stacks <https://jupyter-docker-stacks.readthedocs.io/en/latest/>`_
59
+ and includes *PythonHere* with usage examples.
60
+
61
+ Example command to start the Docker container::
62
+
63
+ docker run \
64
+ --rm \
65
+ -p 8888:8888 \
66
+ --user root \
67
+ -e CHOWN_EXTRA=/home/jovyan/work \
68
+ -e CHOWN_EXTRA_OPTS='-R' \
69
+ -v "$(pwd)/work":/home/jovyan/work \
70
+ herethere/pythonhere:latest
71
+
72
+ The command exposes the Jupyter server on host port ``8888``. Jupyter logs are
73
+ printed in the terminal and include a URL such as
74
+ ``http://127.0.0.1:8888/?token=...``. Open this URL in a browser to use the
75
+ local Jupyter environment.
76
+
77
+ Files in ``/home/jovyan/work`` inside the container are stored in the local
78
+ ``work`` directory.
79
+
80
+
81
+ Run a local Jupyter environment without Docker
82
+ ----------------------------------------------
83
+
84
+ Commands to run locally::
85
+
86
+ pip install pythonhere jupyter
87
+ jupyter notebook
88
+
89
+
90
+ Build Android app
91
+ -----------------
92
+
93
+ To build with `Buildozer <https://github.com/kivy/buildozer>`_, run in the source directory::
94
+
95
+
96
+ buildozer android debug
97
+
98
+
99
+ Related resources
100
+ -----------------
101
+
102
+ * `Kivy Remote Shell <https://github.com/kivy/kivy-remote-shell>`_ : Remote SSH+Python interactive shell application
103
+ * `herethere <https://github.com/b3b/herethere>`_ : Library for interactive code execution, based on AsyncSSH
104
+ * `AsyncSSH <https://github.com/ronf/asyncssh>`_ : Asynchronous SSH for Python
@@ -0,0 +1,151 @@
1
+ [build-system]
2
+ requires = ["setuptools>=70", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pythonhere"
7
+ dynamic = ["version"]
8
+ description = "Here is the Kivy based app to run code from the Jupyter magic %there"
9
+ readme = { file = "README.rst", content-type = "text/x-rst" }
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ authors = [
13
+ { name = "b3b", email = "ash.b3b@gmail.com" },
14
+ ]
15
+ keywords = ["android", "ipython", "jupyter", "magic", "kivy"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Programming Language :: Python :: 3.10",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Programming Language :: Python :: 3.14",
23
+ ]
24
+ dependencies = [
25
+ "herethere[magic]>=0.2.1",
26
+ "ipython",
27
+ "ipywidgets",
28
+ "Pillow",
29
+ ]
30
+
31
+ [dependency-groups]
32
+ dev = [
33
+ { include-group = "docker" },
34
+ "build",
35
+ "codecov",
36
+ "docutils",
37
+ "ifaddr",
38
+ "kivy==2.3.1",
39
+ "pylint",
40
+ "pytest",
41
+ "pytest-asyncio",
42
+ "pytest-cov",
43
+ "pytest-mock",
44
+ "ruff",
45
+ "twine",
46
+ ]
47
+ docker = [
48
+ "jupytext==1.19.3",
49
+ ]
50
+
51
+ [project.urls]
52
+ Homepage = "https://github.com/b3b/ipython-pythonhere"
53
+ Changelog = "https://github.com/b3b/pythonhere/blob/master/CHANGELOG.rst"
54
+
55
+ [tool.uv]
56
+ environments = [
57
+ "sys_platform == 'linux'",
58
+ ]
59
+
60
+ [tool.pytest.ini_options]
61
+ asyncio_mode = "auto"
62
+ pythonpath = ["pythonhere"]
63
+
64
+ [tool.setuptools]
65
+ packages = [
66
+ "pythonhere",
67
+ "pythonhere.magic_here",
68
+ "pythonhere.ui_here",
69
+ ]
70
+
71
+ [tool.setuptools.dynamic]
72
+ version = { attr = "pythonhere.version_here.__version__" }
73
+
74
+ [tool.setuptools.package-data]
75
+ pythonhere = [
76
+ "*.kv",
77
+ "data/logo/*.png",
78
+ "ui_here/*.kv",
79
+ ]
80
+
81
+ [tool.ruff]
82
+ line-length = 88
83
+ target-version = "py310"
84
+
85
+ [tool.ruff.lint]
86
+ select = [
87
+ "E", # pycodestyle errors
88
+ "F", # pyflakes
89
+ "W", # pycodestyle warnings
90
+ "I", # import sorting
91
+ "UP", # pyupgrade
92
+ "B", # flake8-bugbear
93
+ "TRY", # exception handling checks
94
+ "PL", # pylint-style checks supported by Ruff
95
+ ]
96
+ ignore = [
97
+ "PLC0207", # keep simple last-part string splits when they are clearer
98
+ "PLC0415", # allow delayed Kivy/Android imports that control side effects
99
+ "TRY002", # keep existing broad app-facing exceptions for now
100
+ "TRY003", # allow direct exception messages in this small app
101
+ ]
102
+
103
+ [tool.ruff.lint.per-file-ignores]
104
+ "tests/**/*.py" = [
105
+ "PLR0913", # parametrized tests naturally pass several fixtures/values
106
+ "PLR2004", # literal expected values are readable in tests
107
+ "PLW0603", # tests may intentionally exercise global namespace behavior
108
+ ]
109
+
110
+ [tool.ruff.format]
111
+ quote-style = "double"
112
+ indent-style = "space"
113
+
114
+ [tool.pylint.main]
115
+ fail-under = 10.0
116
+ ignore = ["migrations"]
117
+ py-version = "3.10"
118
+
119
+ [tool.pylint."messages control"]
120
+ disable = [
121
+ "attribute-defined-outside-init",
122
+ "bad-inline-option",
123
+ "broad-exception-caught",
124
+ "broad-exception-raised",
125
+ "deprecated-pragma",
126
+ "duplicate-code",
127
+ "file-ignored",
128
+ "import-outside-toplevel",
129
+ "import-error",
130
+ "inconsistent-return-statements",
131
+ "invalid-name",
132
+ "locally-disabled",
133
+ "logging-too-few-args",
134
+ "missing-function-docstring",
135
+ "missing-module-docstring",
136
+ "no-member",
137
+ "possibly-used-before-assignment",
138
+ "protected-access",
139
+ "raw-checker-failed",
140
+ "suppressed-message",
141
+ "too-few-public-methods",
142
+ "too-many-instance-attributes",
143
+ "unnecessary-ellipsis",
144
+ "unused-argument",
145
+ "unused-wildcard-import",
146
+ "use-symbolic-message-instead",
147
+ "useless-suppression",
148
+ "wildcard-import",
149
+ "wrong-import-order",
150
+ ]
151
+ enable = ["c-extension-no-member"]
@@ -1,14 +1,13 @@
1
1
  """Android specific functions."""
2
+
2
3
  # pylint: disable=invalid-name,import-error,import-outside-toplevel
3
- from pathlib import Path
4
- from typing import Optional
5
4
  import uuid
5
+ from pathlib import Path
6
6
 
7
7
  from android import activity as android_activity
8
8
  from jnius import autoclass, cast
9
9
  from kivy.logger import Logger
10
10
 
11
-
12
11
  Context = autoclass("android.content.Context")
13
12
  Icon = autoclass("android.graphics.drawable.Icon")
14
13
  Intent = autoclass("android.content.Intent")
@@ -23,7 +22,7 @@ def get_current_intent() -> Intent:
23
22
  return PythonActivity.mActivity.getIntent()
24
23
 
25
24
 
26
- def get_startup_script(intent: None = None) -> Optional[str]:
25
+ def get_startup_script(intent: Intent | None = None) -> str | None:
27
26
  """Return script entrypoint that was passed to a given, or current, intent."""
28
27
  if not intent:
29
28
  intent = get_current_intent()
@@ -36,8 +35,7 @@ def restart_app(script: str = None):
36
35
  Logger.info("PythonHere: restart requested with a script: %s", script)
37
36
  activity = PythonActivity.mActivity
38
37
  intent = Intent(activity.getApplicationContext(), PythonActivity)
39
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
40
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
38
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
41
39
 
42
40
  if script:
43
41
  intent.setData(Uri.parse(script))
@@ -60,11 +58,17 @@ def bind_run_script_on_new_intent():
60
58
 
61
59
 
62
60
  def create_shortcut_icon() -> Icon:
63
- """Create icon to use for a shurtcut."""
61
+ """Create icon to use for a shortcut."""
64
62
  activity = PythonActivity.mActivity
65
- Drawable = autoclass("{}.R$drawable".format(activity.getPackageName()))
66
63
  context = cast("android.content.Context", activity.getApplicationContext())
67
- return Icon.createWithResource(context, Drawable.icon)
64
+
65
+ app_info = context.getApplicationInfo()
66
+ icon_res_id = app_info.icon
67
+
68
+ if not icon_res_id:
69
+ raise RuntimeError("Application icon resource id was not found")
70
+
71
+ return Icon.createWithResource(context, icon_res_id)
68
72
 
69
73
 
70
74
  def resolve_script_path(script: str) -> str:
@@ -98,4 +102,6 @@ def pin_shortcut(script: str, label: str):
98
102
  )
99
103
 
100
104
  manager = activity.getSystemService(Context.SHORTCUT_SERVICE)
105
+ if not manager.isRequestPinShortcutSupported():
106
+ raise RuntimeError("Pinned shortcuts are not supported by this launcher")
101
107
  manager.requestPinShortcut(shortcut, None)
@@ -1,4 +1,5 @@
1
1
  """Enums."""
2
+
2
3
  from enum import Enum
3
4
 
4
5
 
@@ -0,0 +1,22 @@
1
+ <-UnhandledExceptionPopupHere>:
2
+ title: "Unhandled Exception catched"
3
+ BoxLayout:
4
+ orientation: 'vertical'
5
+ padding: 10
6
+ spacing: 20
7
+ Label:
8
+ size_hint_y: None
9
+ font_size: '18sp'
10
+ height: '24sp'
11
+ text: 'Exception details: '
12
+ ScrollView:
13
+ CodeInput:
14
+ id: catched_exception_code_input_here
15
+ text: root.message
16
+ size_hint: 1, None
17
+ height: self.minimum_height
18
+ Button:
19
+ size_hint_y: None
20
+ height: '40sp'
21
+ text: 'OK, continue'
22
+ on_press: root.dismiss()
@@ -1,46 +1,26 @@
1
1
  """App exceptions manager."""
2
+
2
3
  import asyncio
3
4
  import traceback
4
- from typing import Optional
5
+ from pathlib import Path
5
6
 
6
7
  from kivy.base import (
7
8
  ExceptionHandler,
8
9
  ExceptionManager,
9
10
  )
10
11
  from kivy.clock import Clock
11
- from kivy.properties import StringProperty # pylint: disable=no-name-in-module
12
12
  from kivy.lang import Builder
13
13
  from kivy.logger import Logger
14
+ from kivy.properties import StringProperty # pylint: disable=no-name-in-module
14
15
  from kivy.uix.popup import Popup
15
16
 
16
17
 
17
18
  def load_exception_popup_style():
18
19
  """Load KV rules for `UnhandledExceptionPopupHere`."""
19
- Builder.load_string(
20
- """<-UnhandledExceptionPopupHere>:
21
- title: "Unhandled Exception catched"
22
- BoxLayout:
23
- orientation: 'vertical'
24
- padding: 10
25
- spacing: 20
26
- Label:
27
- size_hint_y: None
28
- font_size: '18sp'
29
- height: '24sp'
30
- text: 'Exception details: '
31
- ScrollView:
32
- CodeInput:
33
- id: catched_exception_code_input_here
34
- text: root.message
35
- size_hint: 1, None
36
- height: self.minimum_height
37
- Button:
38
- size_hint_y: None
39
- height: '40sp'
40
- text: 'OK, continue'
41
- on_press: root.dismiss()
42
- """
43
- )
20
+ kv_path = str(Path(__file__).with_suffix(".kv"))
21
+
22
+ if kv_path not in Builder.files:
23
+ Builder.load_file(kv_path)
44
24
 
45
25
 
46
26
  class ErrorMessageOnException(ExceptionHandler):
@@ -48,9 +28,9 @@ class ErrorMessageOnException(ExceptionHandler):
48
28
 
49
29
  def handle_exception(self, exception) -> int:
50
30
  """Handle a exception."""
51
- Logger.exception("Unhandled Exception catched")
52
31
  if isinstance(exception, (asyncio.CancelledError, KeyboardInterrupt)):
53
32
  return ExceptionManager.RAISE
33
+ Logger.exception("Unhandled Exception catched")
54
34
  show_exception_popup()
55
35
  return ExceptionManager.PASS
56
36
 
@@ -63,10 +43,14 @@ class UnhandledExceptionPopupHere(Popup):
63
43
 
64
44
  def install_exception_handler():
65
45
  """Install `ErrorMessageOnException` exception handler."""
66
- ExceptionManager.add_handler(ErrorMessageOnException())
46
+ if not any(
47
+ isinstance(handler, ErrorMessageOnException)
48
+ for handler in ExceptionManager.handlers
49
+ ):
50
+ ExceptionManager.add_handler(ErrorMessageOnException())
67
51
 
68
52
 
69
- def show_exception_popup(exc: Optional[Exception] = None):
53
+ def show_exception_popup(exc: Exception | None = None):
70
54
  """Show exception popup."""
71
55
  load_exception_popup_style()
72
56
  if exc:
@@ -4,11 +4,10 @@
4
4
  from base64 import b64decode
5
5
  from io import BytesIO, StringIO
6
6
 
7
- from PIL import Image as PILImage
8
- from IPython.display import display
9
7
  import click
10
- from herethere.there.commands import there_group, there_code_shortcut
11
-
8
+ from herethere.there.commands import there_code_shortcut, there_group
9
+ from IPython.display import display
10
+ from PIL import Image as PILImage
12
11
 
13
12
  KV_COMMAND_TEMPLATE = r"""
14
13
  from window_here import load_kv_string
@@ -67,7 +66,9 @@ def screenshot(ctx, width, output):
67
66
  img = PILImage.open(BytesIO(data)).convert("RGB")
68
67
  if width:
69
68
  height = int(width * img.size[1] // img.size[0])
70
- img = img.resize((width, height), PILImage.ANTIALIAS)
69
+ resampling = getattr(PILImage, "Resampling", PILImage)
70
+ resample_filter = getattr(resampling, "LANCZOS", PILImage.LANCZOS)
71
+ img = img.resize((width, height), resample_filter)
71
72
 
72
73
  if output:
73
74
  img.save(output)