pythonhere 0.1.4__py3-none-any.whl → 0.2.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.
- pythonhere/__init__.py +1 -1
- pythonhere/android_here.py +100 -0
- pythonhere/data/logo/logo-128.png +0 -0
- pythonhere/data/logo/logo-32.png +0 -0
- pythonhere/data/logo/logo-splash.png +0 -0
- pythonhere/enum_here.py +1 -0
- pythonhere/exception_manager_here.kv +22 -0
- pythonhere/exception_manager_here.py +14 -30
- pythonhere/launcher_here.py +46 -0
- pythonhere/magic_here/shortcuts.py +21 -5
- pythonhere/main.py +131 -34
- pythonhere/network_here.py +4 -3
- pythonhere/patches_here.py +1 -2
- pythonhere/pythonhere.kv +49 -0
- pythonhere/server_here.py +29 -26
- pythonhere/ui_here/actionbar_here.kv +8 -0
- pythonhere/ui_here/common_here.kv +14 -0
- pythonhere/ui_here/connection_address_here.kv +32 -0
- pythonhere/ui_here/connection_address_here.py +2 -2
- pythonhere/ui_here/layout_here.py +1 -0
- pythonhere/ui_here/server_screen_here.kv +57 -0
- pythonhere/ui_here/server_screen_here.py +4 -3
- pythonhere/ui_here/settings_here.kv +31 -0
- pythonhere/ui_here/settings_here.py +15 -6
- pythonhere/version_here.py +1 -1
- pythonhere/window_here.py +10 -3
- pythonhere-0.2.0.dist-info/METADATA +125 -0
- pythonhere-0.2.0.dist-info/RECORD +33 -0
- {pythonhere-0.1.4.dist-info → pythonhere-0.2.0.dist-info}/WHEEL +1 -1
- pythonhere-0.1.4.dist-info/METADATA +0 -139
- pythonhere-0.1.4.dist-info/RECORD +0 -21
- {pythonhere-0.1.4.dist-info → pythonhere-0.2.0.dist-info/licenses}/LICENSE +0 -0
- {pythonhere-0.1.4.dist-info → pythonhere-0.2.0.dist-info}/top_level.txt +0 -0
pythonhere/server_here.py
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"""SSH server."""
|
|
2
|
+
|
|
2
3
|
import asyncio
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
|
|
6
|
+
from exception_manager_here import show_exception_popup
|
|
7
|
+
from herethere.here.server import ServerConfig, SSHServerHere, start_server
|
|
5
8
|
from kivy.app import App
|
|
6
9
|
from kivy.logger import Logger
|
|
7
|
-
from herethere.here.server import SSHServerHere, ServerConfig, start_server
|
|
8
|
-
|
|
9
|
-
from exception_manager_here import show_exception_popup
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class PythonHereServer(SSHServerHere):
|
|
@@ -21,28 +21,31 @@ class PythonHereServer(SSHServerHere):
|
|
|
21
21
|
async def run_ssh_server(app):
|
|
22
22
|
"""Start and run SSH server."""
|
|
23
23
|
Logger.debug("PythonHere: wait for %here settings")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
24
|
+
while not app.ssh_server_started.is_set():
|
|
25
|
+
try:
|
|
26
|
+
await app.ssh_server_config_ready.wait()
|
|
27
|
+
except asyncio.CancelledError:
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
config = ServerConfig(
|
|
32
|
+
host="",
|
|
33
|
+
chroot=app.upload_dir,
|
|
34
|
+
key_path=Path("./key.rsa").resolve(),
|
|
35
|
+
**app.get_pythonhere_config(),
|
|
36
|
+
)
|
|
37
|
+
server = await start_server(
|
|
38
|
+
config,
|
|
39
|
+
namespace=app.ssh_server_namespace,
|
|
40
|
+
server_factory=PythonHereServer,
|
|
41
|
+
)
|
|
42
|
+
except Exception as exc:
|
|
43
|
+
Logger.error("PythonHere: SSH server start error")
|
|
44
|
+
Logger.exception(exc)
|
|
45
|
+
show_exception_popup(exc)
|
|
46
|
+
app.ssh_server_config_ready.clear()
|
|
47
|
+
else:
|
|
48
|
+
app.ssh_server_started.set()
|
|
46
49
|
|
|
47
50
|
try:
|
|
48
51
|
await server.wait_closed()
|
|
@@ -50,7 +53,7 @@ async def run_ssh_server(app):
|
|
|
50
53
|
Logger.info("PythonHere: SSH server task canceled")
|
|
51
54
|
await server.stop()
|
|
52
55
|
except Exception as exc:
|
|
53
|
-
Logger.
|
|
56
|
+
Logger.error("PythonHere: SSH server stop by exception")
|
|
54
57
|
Logger.exception(exc)
|
|
55
58
|
show_exception_popup(exc)
|
|
56
59
|
Logger.info("PythonHere: SSH server closed")
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#:kivy 1.0
|
|
2
|
+
# Common settings
|
|
3
|
+
|
|
4
|
+
#:import hex kivy.utils.get_color_from_hex
|
|
5
|
+
#:import sp kivy.metrics.sp
|
|
6
|
+
|
|
7
|
+
#:set bg_color "#EEEEEE"
|
|
8
|
+
#:set pallete ["#646464", "#306998", "#4B8BBE", "#FFD43B", "#FFE873"]
|
|
9
|
+
#:set tc lambda text, index: f"[color={pallete[index]}]{text}[/color]"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
<Label>:
|
|
13
|
+
font_name: "DejaVuSans"
|
|
14
|
+
markup: True
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#:kivy 1.0
|
|
2
|
+
#:import ConnectionAddressLabel ui_here.connection_address_here.ConnectionAddressLabel
|
|
3
|
+
#:import ConnectionAddressInfoBox ui_here.connection_address_here.ConnectionAddressInfoBox
|
|
4
|
+
|
|
5
|
+
<ConnectionAddressLabel>:
|
|
6
|
+
text: f"{tc('↳', 3)} {tc(self.address, 2)} ({self.interface})"
|
|
7
|
+
size_hint: 1, None
|
|
8
|
+
text_size: self.size
|
|
9
|
+
height: self.texture_size[1]
|
|
10
|
+
font_size: "20sp"
|
|
11
|
+
|
|
12
|
+
<ConnectionAddressInfoBox>:
|
|
13
|
+
cols: 1
|
|
14
|
+
size_hint: 1, 1
|
|
15
|
+
padding: '20sp', 0, 0, 0
|
|
16
|
+
|
|
17
|
+
Label:
|
|
18
|
+
text: f"Server is running.\nConnect {tc('here', 3)} via"
|
|
19
|
+
size_hint: 1, None
|
|
20
|
+
text_size: self.size
|
|
21
|
+
height: "80sp"
|
|
22
|
+
font_size: "30sp"
|
|
23
|
+
|
|
24
|
+
ScrollView:
|
|
25
|
+
do_scroll_x: False
|
|
26
|
+
size_hint: 1, 1
|
|
27
|
+
|
|
28
|
+
BoxLayout:
|
|
29
|
+
id: address_list
|
|
30
|
+
orientation: "vertical"
|
|
31
|
+
size_hint: 1, None
|
|
32
|
+
height: self.minimum_size[1]
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"""Connection information widgets."""
|
|
2
|
+
|
|
2
3
|
from kivy.clock import Clock, mainthread
|
|
3
4
|
from kivy.logger import Logger
|
|
4
5
|
from kivy.properties import StringProperty # pylint: disable=no-name-in-module
|
|
5
|
-
from kivy.uix.label import Label
|
|
6
6
|
from kivy.uix.gridlayout import GridLayout
|
|
7
|
-
|
|
7
|
+
from kivy.uix.label import Label
|
|
8
8
|
from network_here import get_all_available_ipv4_adrresses
|
|
9
9
|
|
|
10
10
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#:import FallOutTransition kivy.uix.screenmanager.FallOutTransition
|
|
2
|
+
#:import App kivy.app.App
|
|
3
|
+
|
|
4
|
+
<LoadingImage@Image>:
|
|
5
|
+
source: "data/images/image-loading.zip"
|
|
6
|
+
allow_stretch: True
|
|
7
|
+
pos_hint: {'center_x': 0.5, 'center_y': 0.4}
|
|
8
|
+
size_hint: None, None
|
|
9
|
+
size: sp(100), sp(100)
|
|
10
|
+
|
|
11
|
+
<StackLabel@Label>:
|
|
12
|
+
size_hint: None, None
|
|
13
|
+
font_size: "20sp"
|
|
14
|
+
size: self.texture_size
|
|
15
|
+
|
|
16
|
+
<ServerNotConfigured@Screen>:
|
|
17
|
+
StackLayout:
|
|
18
|
+
orientation: "lr-tb"
|
|
19
|
+
padding: sp(20), sp(20), 0, 0
|
|
20
|
+
StackLabel:
|
|
21
|
+
text: "To start "
|
|
22
|
+
StackLabel:
|
|
23
|
+
text: f"{tc('Python', 1)}{tc('here', 3)}, "
|
|
24
|
+
StackLabel:
|
|
25
|
+
text: "you "
|
|
26
|
+
StackLabel:
|
|
27
|
+
text: "need to "
|
|
28
|
+
StackLabel:
|
|
29
|
+
text: "edit the "
|
|
30
|
+
Button:
|
|
31
|
+
text: "Settings"
|
|
32
|
+
font_size: "20sp"
|
|
33
|
+
size_hint: None, None
|
|
34
|
+
width: self.texture_size[0] + sp(20)
|
|
35
|
+
height: sp(30)
|
|
36
|
+
on_release: App.get_running_app().root.switch_screen(ScreenName.settings)
|
|
37
|
+
StackLabel:
|
|
38
|
+
text: " section."
|
|
39
|
+
|
|
40
|
+
<StartingServerScreen@Screen>:
|
|
41
|
+
Label:
|
|
42
|
+
text: f"Waiting {tc('Python', 1)}{tc('here', 3)} to start"
|
|
43
|
+
font_size: "30sp"
|
|
44
|
+
LoadingImage:
|
|
45
|
+
|
|
46
|
+
<ServerScreenManager>:
|
|
47
|
+
transition: FallOutTransition()
|
|
48
|
+
Screen:
|
|
49
|
+
LoadingImage:
|
|
50
|
+
ServerNotConfigured:
|
|
51
|
+
name: ServerState.not_configured
|
|
52
|
+
Screen:
|
|
53
|
+
StartingServerScreen:
|
|
54
|
+
name: ServerState.starting_server
|
|
55
|
+
Screen:
|
|
56
|
+
name: ServerState.ready
|
|
57
|
+
ConnectionAddressInfoBox:
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""%here server screen."""
|
|
2
|
-
from kivy.app import App
|
|
3
|
-
from kivy.clock import Clock
|
|
4
|
-
from kivy.uix.screenmanager import ScreenManager
|
|
5
2
|
|
|
6
3
|
from enum_here import ServerState
|
|
4
|
+
from kivy.app import App
|
|
5
|
+
from kivy.clock import Clock, mainthread
|
|
6
|
+
from kivy.uix.screenmanager import ScreenManager
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class ServerScreenManager(ScreenManager):
|
|
@@ -13,6 +13,7 @@ class ServerScreenManager(ScreenManager):
|
|
|
13
13
|
super().__init__(*args, **kwargs)
|
|
14
14
|
self.update_event = Clock.schedule_interval(self.update, 0.5)
|
|
15
15
|
|
|
16
|
+
@mainthread
|
|
16
17
|
def update(self, _=None):
|
|
17
18
|
"""Determines server state, and switch to appropriate screen."""
|
|
18
19
|
app = App.get_running_app()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#:kivy 1.0
|
|
2
|
+
#:import App kivy.app.App
|
|
3
|
+
#:import SettingPassword ui_here.settings_here.SettingPassword
|
|
4
|
+
|
|
5
|
+
<SettingPassword>:
|
|
6
|
+
PasswordLabel:
|
|
7
|
+
pos: root.pos
|
|
8
|
+
font_size: "15sp"
|
|
9
|
+
text: '*' * len(root.value or '')
|
|
10
|
+
|
|
11
|
+
<SettingButton>:
|
|
12
|
+
size_hint: 1, None
|
|
13
|
+
height: sp(60)
|
|
14
|
+
opacity: 1 if self.active else 0
|
|
15
|
+
|
|
16
|
+
Button:
|
|
17
|
+
size_hint: None, None
|
|
18
|
+
width: self.texture_size[0] + sp(20)
|
|
19
|
+
height: sp(40)
|
|
20
|
+
halign: "center"
|
|
21
|
+
text: root.title
|
|
22
|
+
disabled: not root.active
|
|
23
|
+
on_release: root.on_release()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
<StartServersettingButton>:
|
|
27
|
+
title: f"{tc('▶', 3)} Start the server"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
<ShowPolicySettingButton>:
|
|
31
|
+
title: "Open privacy\n policy in browser"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Settings panel widgets."""
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import webbrowser
|
|
4
|
+
from typing import Any
|
|
4
5
|
|
|
5
6
|
from kivy.app import App
|
|
6
7
|
from kivy.config import Config
|
|
@@ -11,11 +12,9 @@ from kivy.properties import ( # pylint: disable=no-name-in-module
|
|
|
11
12
|
)
|
|
12
13
|
from kivy.uix.anchorlayout import AnchorLayout
|
|
13
14
|
from kivy.uix.label import Label
|
|
15
|
+
from kivy.uix.popup import Popup
|
|
14
16
|
from kivy.uix.settings import Settings, SettingString
|
|
15
17
|
|
|
16
|
-
from enum_here import ScreenName
|
|
17
|
-
|
|
18
|
-
|
|
19
18
|
SETTINGS_HERE = """
|
|
20
19
|
[
|
|
21
20
|
{
|
|
@@ -97,8 +96,18 @@ class StartServerSettingButton(SettingButton):
|
|
|
97
96
|
def on_release(self):
|
|
98
97
|
"""Start the server."""
|
|
99
98
|
app = App.get_running_app()
|
|
99
|
+
if app.ssh_server_started.is_set():
|
|
100
|
+
popup = Popup(
|
|
101
|
+
title="Server is already started",
|
|
102
|
+
content=Label(
|
|
103
|
+
text="New settings takes effect\n after application restart."
|
|
104
|
+
),
|
|
105
|
+
size_hint=(None, None),
|
|
106
|
+
size=("250sp", "250sp"),
|
|
107
|
+
)
|
|
108
|
+
popup.open()
|
|
109
|
+
return
|
|
100
110
|
app.update_server_config_status()
|
|
101
|
-
app.root.switch_screen(ScreenName.here)
|
|
102
111
|
|
|
103
112
|
|
|
104
113
|
class ShowPolicySettingButton(SettingButton):
|
|
@@ -128,7 +137,7 @@ class SettingsHere(Settings):
|
|
|
128
137
|
self.add_kivy_panel()
|
|
129
138
|
self.add_json_panel("Privacy Policy", Config, data=SETTINGS_PRIVACY)
|
|
130
139
|
|
|
131
|
-
def get_pythonhere_config(self) ->
|
|
140
|
+
def get_pythonhere_config(self) -> dict[str, Any]:
|
|
132
141
|
"""Extract server parts of the config."""
|
|
133
142
|
return {
|
|
134
143
|
"username": Config.get("pythonhere", "username"),
|
pythonhere/version_here.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.2.0"
|
pythonhere/window_here.py
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
"""Utilities for working with Kivy window."""
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import os
|
|
4
|
-
from pathlib import Path
|
|
5
4
|
import time
|
|
5
|
+
from base64 import b64encode
|
|
6
|
+
from pathlib import Path
|
|
6
7
|
|
|
7
8
|
from kivy.app import App
|
|
8
|
-
from kivy.core.window import Window
|
|
9
9
|
from kivy.lang import Builder
|
|
10
10
|
from kivy.uix.boxlayout import BoxLayout
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def reset_window_environment() -> BoxLayout:
|
|
14
14
|
"""Remove PythonHere app widgets and styles."""
|
|
15
|
+
# import Window inside function to avoid early loading of the app config
|
|
16
|
+
from kivy.core.window import Window # pylint: disable=import-outside-toplevel
|
|
17
|
+
|
|
15
18
|
for widget in Window.children:
|
|
16
19
|
widget.clear_widgets()
|
|
17
20
|
Window.remove_widget(widget)
|
|
@@ -30,6 +33,8 @@ def unload_app_kv_styles():
|
|
|
30
33
|
|
|
31
34
|
def load_kv_string(code: str, clear_style: bool):
|
|
32
35
|
"""Insert given rules into the Kivy Language Builder."""
|
|
36
|
+
from kivy.core.window import Window # pylint: disable=import-outside-toplevel
|
|
37
|
+
|
|
33
38
|
app = App.get_running_app()
|
|
34
39
|
|
|
35
40
|
if clear_style:
|
|
@@ -50,6 +55,8 @@ def load_kv_string(code: str, clear_style: bool):
|
|
|
50
55
|
|
|
51
56
|
def encoded_screenshot() -> str:
|
|
52
57
|
"""Return base64 encoded displayed image."""
|
|
58
|
+
from kivy.core.window import Window # pylint: disable=import-outside-toplevel
|
|
59
|
+
|
|
53
60
|
path = str(Path(f"screenshot_{time.time()}.png").resolve())
|
|
54
61
|
Window.children[0].export_to_png(path)
|
|
55
62
|
with open(path, "rb") as png_file:
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pythonhere
|
|
3
|
+
Version: 0.2.0
|
|
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.1.0
|
|
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
|
+
Ready-to-use *PythonHere* APKs are available from the `GitHub Releases <https://github.com/b3b/pythonhere/releases>`_ page.
|
|
72
|
+
|
|
73
|
+
For APK provenance and signing checks, see `Android APK verification <https://github.com/b3b/pythonhere/blob/master/docs/android-apk-verification.rst>`_.
|
|
74
|
+
For a list of Python packages included in the Android build, see `buildozer.spec <https://github.com/b3b/pythonhere/blob/master/buildozer.spec>`_.
|
|
75
|
+
|
|
76
|
+
Start a local Jupyter environment with Docker
|
|
77
|
+
---------------------------------------------
|
|
78
|
+
|
|
79
|
+
The Docker image is based on `Jupyter Docker Stacks <https://jupyter-docker-stacks.readthedocs.io/en/latest/>`_
|
|
80
|
+
and includes *PythonHere* with usage examples.
|
|
81
|
+
|
|
82
|
+
Example command to start the Docker container::
|
|
83
|
+
|
|
84
|
+
docker run \
|
|
85
|
+
--rm \
|
|
86
|
+
-p 8888:8888 \
|
|
87
|
+
--user root \
|
|
88
|
+
-e CHOWN_EXTRA=/home/jovyan/work \
|
|
89
|
+
-e CHOWN_EXTRA_OPTS='-R' \
|
|
90
|
+
-v "$(pwd)/work":/home/jovyan/work \
|
|
91
|
+
herethere/pythonhere:latest
|
|
92
|
+
|
|
93
|
+
The command exposes the Jupyter server on host port ``8888``. Jupyter logs are
|
|
94
|
+
printed in the terminal and include a URL such as
|
|
95
|
+
``http://127.0.0.1:8888/?token=...``. Open this URL in a browser to use the
|
|
96
|
+
local Jupyter environment.
|
|
97
|
+
|
|
98
|
+
Files in ``/home/jovyan/work`` inside the container are stored in the local
|
|
99
|
+
``work`` directory.
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
Run a local Jupyter environment without Docker
|
|
103
|
+
----------------------------------------------
|
|
104
|
+
|
|
105
|
+
Commands to run locally::
|
|
106
|
+
|
|
107
|
+
pip install pythonhere jupyter
|
|
108
|
+
jupyter notebook
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
Build Android app
|
|
112
|
+
-----------------
|
|
113
|
+
|
|
114
|
+
To build with `Buildozer <https://github.com/kivy/buildozer>`_, run in the source directory::
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
buildozer android debug
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
Related resources
|
|
121
|
+
-----------------
|
|
122
|
+
|
|
123
|
+
* `Kivy Remote Shell <https://github.com/kivy/kivy-remote-shell>`_ : Remote SSH+Python interactive shell application
|
|
124
|
+
* `herethere <https://github.com/b3b/herethere>`_ : Library for interactive code execution, based on AsyncSSH
|
|
125
|
+
* `AsyncSSH <https://github.com/ronf/asyncssh>`_ : Asynchronous SSH for Python
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
pythonhere/__init__.py,sha256=gkVrYFkF3klE1fripHpmTuZx8_BaVHWRRvvr6PK6XWE,212
|
|
2
|
+
pythonhere/android_here.py,sha256=imQCNkhAJYgT3gMeX25kd9cQzH5ZbeF5PlgP8e9sbl0,3316
|
|
3
|
+
pythonhere/enum_here.py,sha256=FPj3pBeXfpPwl4-LDdC-wmbaoiv4QdbKHkGoBfTXnTE,449
|
|
4
|
+
pythonhere/exception_manager_here.kv,sha256=FZNhQgVojSLcVxZ0vjAxAF915xCBP-3nldPXVczRzCE,656
|
|
5
|
+
pythonhere/exception_manager_here.py,sha256=-UYqGRbzkoyfF0-pLWbxaFGEAvAdaw6b8hkm_bMIKx8,1943
|
|
6
|
+
pythonhere/launcher_here.py,sha256=I5zBpN0P3hmhsZFOJemxQ9Q2k2am5zkfS6Vg0Svnl98,1274
|
|
7
|
+
pythonhere/main.py,sha256=7vAF6mznCr2JFxeXjm3-a6PN1abPt-loI8ZHncAAPp0,7237
|
|
8
|
+
pythonhere/network_here.py,sha256=e3FDlBcXPaTFlWaqW2_bcWctht-ifpFIvtOj0lO4S9w,1558
|
|
9
|
+
pythonhere/patches_here.py,sha256=zB7Ohbk5PIXt0imnnrV5Mtl-Y7jpD_p5IBPlijQcCCQ,1727
|
|
10
|
+
pythonhere/pythonhere.kv,sha256=x1xwiKvJAevqBhtXGLb_axIf-VRos4V_jM9Q2i44f2U,1453
|
|
11
|
+
pythonhere/server_here.py,sha256=ss4Alg4ySkI0aG58l8D2SQMXiGHJeK-hGUIIBrbQ6nE,1854
|
|
12
|
+
pythonhere/version_here.py,sha256=Zn1KFblwuFHiDRdRAiRnDBRkbPttWh44jKa5zG2ov0E,22
|
|
13
|
+
pythonhere/window_here.py,sha256=na47v7bgomAjb6VC_g35RwhzEtm5RCInQIuQK27zY8U,2023
|
|
14
|
+
pythonhere/data/logo/logo-128.png,sha256=4sitDJcVBHOU5UPxicmrzc6YqFY7_xtYrc_ULyrw9ks,4161
|
|
15
|
+
pythonhere/data/logo/logo-32.png,sha256=qanrIAt6vK_2BsCjCxth2B4vxBa0ONnpDlils7syUWE,1247
|
|
16
|
+
pythonhere/data/logo/logo-splash.png,sha256=-edmwKnkkpGP4RZ0AJYto5_BOy8NoISt5ftOEI8BQOY,4663
|
|
17
|
+
pythonhere/magic_here/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
pythonhere/magic_here/shortcuts.py,sha256=pdB_ET6Snl4DxusbyZPI1_G8jGzA8d35er60qQb_2cY,2499
|
|
19
|
+
pythonhere/ui_here/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
pythonhere/ui_here/actionbar_here.kv,sha256=cOjY4ByOyBDUbeS9kbI3JAhi_X3kJ3Pb5h4RL58urwU,214
|
|
21
|
+
pythonhere/ui_here/common_here.kv,sha256=vnNyDwAKKNbRKvh0QKMbTvR0jNDKiY8DdGRnoEEexSc,324
|
|
22
|
+
pythonhere/ui_here/connection_address_here.kv,sha256=T0E8WX6QVvFLB2QL4GP7g9uDwQt5ja7aAODUe8x1NUY,885
|
|
23
|
+
pythonhere/ui_here/connection_address_here.py,sha256=_NM2mJuXM2jgk9eXH8NEV6IxGvJX_fc3wuEq-n7BoLs,1160
|
|
24
|
+
pythonhere/ui_here/layout_here.py,sha256=4sOf4ELSeaJJQkBfgrkM0Cmf6A1bpYTYtZl5GDXGJZE,485
|
|
25
|
+
pythonhere/ui_here/server_screen_here.kv,sha256=x0Se8hmgjS57gFIPa1r2lf6yJchs3lXOrAAtWf6M5Io,1562
|
|
26
|
+
pythonhere/ui_here/server_screen_here.py,sha256=2zQv1YFmiKNxa__7UZDYbAAAwYFLoZPSZPhr7vjCAkU,925
|
|
27
|
+
pythonhere/ui_here/settings_here.kv,sha256=stMcDq8dCYjay4idK30_7EUu5edkMDAQVPv0H0GuAcM,710
|
|
28
|
+
pythonhere/ui_here/settings_here.py,sha256=7NflZBUqY2ANZaSEuubaX9wIKniqbekcNQkUbqhhmto,4093
|
|
29
|
+
pythonhere-0.2.0.dist-info/licenses/LICENSE,sha256=nW9_eVi3dSMOkr6xghsobAovBEJiffjl_5WjujJTa74,1071
|
|
30
|
+
pythonhere-0.2.0.dist-info/METADATA,sha256=s6EVWNzwY9n6PDwLpNX6kVrd55fGxGpba3j26LYz7cQ,4790
|
|
31
|
+
pythonhere-0.2.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
32
|
+
pythonhere-0.2.0.dist-info/top_level.txt,sha256=dvkfRGF1tFbkjXzD9vwqXTge1Znkv7ga5fRJy5yhJsE,11
|
|
33
|
+
pythonhere-0.2.0.dist-info/RECORD,,
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: pythonhere
|
|
3
|
-
Version: 0.1.4
|
|
4
|
-
Summary: Here is the Kivy based app to run code from the Jupyter magic %there
|
|
5
|
-
Home-page: https://github.com/b3b/ipython-pythonhere
|
|
6
|
-
Author: b3b
|
|
7
|
-
Author-email: ash.b3b@gmail.com
|
|
8
|
-
License: MIT
|
|
9
|
-
Project-URL: Changelog, https://github.com/b3b/pythonhere/blob/master/CHANGELOG.rst
|
|
10
|
-
Keywords: android ipython jupyter magic kivy
|
|
11
|
-
Platform: UNKNOWN
|
|
12
|
-
Classifier: Development Status :: 3 - Alpha
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
-
Description-Content-Type: text/x-rst
|
|
16
|
-
Requires-Dist: kivy (>=2.0.0)
|
|
17
|
-
Requires-Dist: herethere (<0.2.0,>=0.1.0)
|
|
18
|
-
Requires-Dist: ifaddr
|
|
19
|
-
Requires-Dist: ipython
|
|
20
|
-
Requires-Dist: ipywidgets
|
|
21
|
-
Requires-Dist: nest-asyncio
|
|
22
|
-
Requires-Dist: Pillow
|
|
23
|
-
Provides-Extra: dev
|
|
24
|
-
Requires-Dist: black ; extra == 'dev'
|
|
25
|
-
Requires-Dist: codecov ; extra == 'dev'
|
|
26
|
-
Requires-Dist: docutils ; extra == 'dev'
|
|
27
|
-
Requires-Dist: flake8 ; extra == 'dev'
|
|
28
|
-
Requires-Dist: jupytext ; extra == 'dev'
|
|
29
|
-
Requires-Dist: pylint ; extra == 'dev'
|
|
30
|
-
Requires-Dist: pytest ; extra == 'dev'
|
|
31
|
-
Requires-Dist: pytest-asyncio ; extra == 'dev'
|
|
32
|
-
Requires-Dist: pytest-cov ; extra == 'dev'
|
|
33
|
-
Requires-Dist: pytest-mock ; extra == 'dev'
|
|
34
|
-
Provides-Extra: docker
|
|
35
|
-
Requires-Dist: jupytext (==1.7.1) ; extra == 'docker'
|
|
36
|
-
|
|
37
|
-
PythonHere
|
|
38
|
-
==========
|
|
39
|
-
|
|
40
|
-
.. start-badges
|
|
41
|
-
.. image:: https://img.shields.io/pypi/status/pythonhere
|
|
42
|
-
:target: https://pypi.python.org/pypi/pythonhere
|
|
43
|
-
:alt: Status
|
|
44
|
-
.. image:: https://img.shields.io/pypi/v/pythonhere.svg
|
|
45
|
-
:target: https://pypi.python.org/pypi/pythonhere
|
|
46
|
-
:alt: Latest version on PyPi
|
|
47
|
-
.. image:: https://img.shields.io/docker/v/herethere/pythonhere?color=%23FFD43B&label=Docker%20Image
|
|
48
|
-
:target: https://hub.docker.com/r/herethere/pythonhere
|
|
49
|
-
:alt: Docker Image Version (latest by date)
|
|
50
|
-
.. image:: https://img.shields.io/pypi/pyversions/pythonhere.svg
|
|
51
|
-
:target: https://pypi.python.org/pypi/pythonhere
|
|
52
|
-
:alt: Supported Python versions
|
|
53
|
-
.. image:: https://github.com/b3b/pythonhere/workflows/ci/badge.svg?branch=master
|
|
54
|
-
:target: https://github.com/b3b/pythonhere/actions?workflow=CI
|
|
55
|
-
:alt: CI Status
|
|
56
|
-
.. image:: https://codecov.io/github/b3b/pythonhere/coverage.svg?branch=master
|
|
57
|
-
:target: https://codecov.io/github/b3b/pythonhere?branch=master
|
|
58
|
-
:alt: Code coverage Status
|
|
59
|
-
.. end-badges
|
|
60
|
-
|
|
61
|
-
*Here* is the `Kivy <https://kivy.org>`_ based app to run Python code from the `Jupyter <https://jupyter.org/>`_ magic %there.
|
|
62
|
-
|
|
63
|
-
- *Here* is a server part with the GUI interface. It could be Android, Raspberry Pi, some other remote device that being debugged.
|
|
64
|
-
- And *%there* is a client - Jupyter magic command to run code interactively on remote device.
|
|
65
|
-
|
|
66
|
-
This app could serve as a Python Kivy playground, for dynamic code execution from the PC.
|
|
67
|
-
|
|
68
|
-
Project documentation: https://herethere.me
|
|
69
|
-
|
|
70
|
-
.. image:: https://raw.githubusercontent.com/b3b/pythonhere/master/docs/description.png
|
|
71
|
-
:alt: Project description
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
Install on Android
|
|
75
|
-
------------------
|
|
76
|
-
|
|
77
|
-
App is available on `Google Play <https://play.google.com/store/apps/details?id=me.herethere.pythonhere>`_.
|
|
78
|
-
|
|
79
|
-
Ready-to-use *PythonHere* APKs are available in the `Releases <https://github.com/b3b/pythonhere/releases>`_ section.
|
|
80
|
-
|
|
81
|
-
For a list of installed Python packages, see: `buildozer.spec <./buildozer.spec>`_.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
Quick Start with Docker
|
|
85
|
-
-----------------------
|
|
86
|
-
|
|
87
|
-
Docker image is based on `Jupyter Docker Stacks <https://jupyter-docker-stacks.readthedocs.io/en/latest/>`_, and includes installed *PythonHere* with usage examples.
|
|
88
|
-
|
|
89
|
-
Example command to start the Docker container::
|
|
90
|
-
|
|
91
|
-
docker run \
|
|
92
|
-
--rm \
|
|
93
|
-
-p 8888:8888 \
|
|
94
|
-
-v "$(pwd)/work":/home/jovyan/work \
|
|
95
|
-
herethere/pythonhere:latest
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
Command will expose the Jupyter Notebook server on host port 8888. Jupyter logs appear in the terminal and include an URL to the notebook server: http://127.0.0.1:8888/?token=... . Visiting this URL in a browser loads the Jupyter Notebook dashboard page.
|
|
99
|
-
|
|
100
|
-
Files from the directory **work** inside container, will be available in the host directory with the same name: **work**.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
Run with Docker Compose
|
|
104
|
-
-----------------------
|
|
105
|
-
|
|
106
|
-
Commands to run with Docker Compose, in the source directory:::
|
|
107
|
-
|
|
108
|
-
cp docker-compose.yml.tmpl docker-compose.yml
|
|
109
|
-
docker-compose up
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
Run locally
|
|
113
|
-
-----------
|
|
114
|
-
|
|
115
|
-
Commands to run locally::
|
|
116
|
-
|
|
117
|
-
pip install pythonhere
|
|
118
|
-
jupyter notebook start
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
Build Android app
|
|
122
|
-
-----------------
|
|
123
|
-
|
|
124
|
-
To build with `Buildozer <https://github.com/kivy/buildozer>`_, run in the source directory::
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
buildozer android debug
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
Related resources
|
|
132
|
-
-----------------
|
|
133
|
-
|
|
134
|
-
* `Kivy Remote Shell <https://github.com/kivy/kivy-remote-shell>`_ : Remote SSH+Python interactive shell application
|
|
135
|
-
* `herethere <https://github.com/b3b/herethere>`_ : Library for interactive code execution, based on AsyncSSH
|
|
136
|
-
* `AsyncSSH <https://github.com/ronf/asyncssh>`_ : Asynchronous SSH for Python
|
|
137
|
-
* `Buildozer action <https://github.com/ArtemSBulgakov/buildozer-action>`_ : GitHub action that is used to build Android APK with Buildozer
|
|
138
|
-
|
|
139
|
-
|