pendify 0.1.3__tar.gz → 0.1.5__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.
- {pendify-0.1.3/src/pendify.egg-info → pendify-0.1.5}/PKG-INFO +9 -3
- {pendify-0.1.3 → pendify-0.1.5}/README.md +8 -2
- {pendify-0.1.3 → pendify-0.1.5}/pyproject.toml +1 -1
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify/__main__.py +72 -8
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify/config.py +18 -6
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify/page.py +132 -25
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify/qr.py +61 -10
- pendify-0.1.5/src/pendify/tray.py +316 -0
- pendify-0.1.5/src/pendify/update.py +252 -0
- {pendify-0.1.3 → pendify-0.1.5/src/pendify.egg-info}/PKG-INFO +9 -3
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify.egg-info/SOURCES.txt +2 -0
- {pendify-0.1.3 → pendify-0.1.5}/LICENSE +0 -0
- {pendify-0.1.3 → pendify-0.1.5}/MANIFEST.in +0 -0
- {pendify-0.1.3 → pendify-0.1.5}/setup.cfg +0 -0
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify/__init__.py +0 -0
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify/alert.py +0 -0
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify/autostart.py +0 -0
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify/client.py +0 -0
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify/codes.py +0 -0
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify/icon.py +0 -0
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify/pairing.py +0 -0
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify/pendify.ico +0 -0
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify/plate_almena.py +0 -0
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify/runfile.py +0 -0
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify/watcher.py +0 -0
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify/worker.py +0 -0
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify.egg-info/dependency_links.txt +0 -0
- {pendify-0.1.3 → pendify-0.1.5}/src/pendify.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pendify
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: Pairs this PC with a phone account by a QR on a loopback page and sends it two kinds of alerts.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/ElkinDev/PendiFy
|
|
@@ -49,11 +49,13 @@ Si Windows responde que no encuentra `python`, usa `py` en su lugar: `py -m pip
|
|
|
49
49
|
|
|
50
50
|
Así no se crean los accesos directos en el Escritorio ni en el menú Inicio, y el inicio con Windows queda en el interruptor de la página. Para actualizar, pulsa «Salir» en la página, ejecuta otra vez la misma línea de pip y vuelve a iniciarlo. Para quitarlo, apaga primero el interruptor «Iniciar con Windows» en la página, pulsa «Salir» y después ejecuta `python -m pip uninstall pendify`. La configuración en `%APPDATA%\pendify`, con el enlace con el teléfono, se queda; puedes borrar esa carpeta a mano.
|
|
51
51
|
|
|
52
|
+
El programa revisa PyPI cada seis horas y, cuando hay una versión más nueva, la instala en segundo plano. La página ofrece entonces «Reiniciar ahora» para empezar a usarla. Para apagarlo, inícialo con `--no-update` o escribe `"update": "off"` en `%APPDATA%\pendify\config.json`. Las actualizaciones automáticas se instalan para tu usuario (--user).
|
|
53
|
+
|
|
52
54
|
### Enlazar
|
|
53
55
|
|
|
54
56
|
Al iniciarse, el navegador abre una página local con un código QR. Escanéalo con la cámara del teléfono: se abre la app Pendi y te pide confirmar el enlace. La página muestra cuando el enlace quedó hecho.
|
|
55
57
|
|
|
56
|
-
El código QR y la clave se muestran durante un minuto al pulsar «Mostrar el código
|
|
58
|
+
El código QR y la clave están ocultos; se muestran durante un minuto al pulsar «Mostrar el código», y la página dice para qué sirve el código.
|
|
57
59
|
|
|
58
60
|
El botón de arriba a la derecha cambia entre el tema claro y el oscuro, y el programa guarda la elección para la próxima vez.
|
|
59
61
|
|
|
@@ -61,6 +63,7 @@ El botón de arriba a la derecha cambia entre el tema claro y el oscuro, y el pr
|
|
|
61
63
|
|
|
62
64
|
- Iniciar: el acceso directo `PendiFy`, o `pythonw -m pendify`, que es lo que ejecuta el acceso directo; sin consola, si ya está abierto o no puede iniciarse te lo dice en una ventana. Para verlo en una consola: `python -m pendify`.
|
|
63
65
|
- Detener: pulsa «Salir» en la página del programa; si no la tienes abierta, iniciarlo otra vez la abre. Como último recurso, cierra el proceso `pythonw.exe` en el Administrador de tareas.
|
|
66
|
+
- Icono junto al reloj: un clic en el icono de PendiFy junto al reloj abre un menú para abrir la página, pausar o reanudar los avisos y salir del programa, también con la página y el navegador cerrados.
|
|
64
67
|
- Pausar: «Pausar avisos», en la tarjeta «Este PC» de la página, deja de leer el cliente del juego: no acepta partidas ni avisa a tu teléfono, y la página sigue abierta. «Reanudar avisos» vuelve a leerlo. La pausa no se guarda: cada vez que el programa se inicia, empieza activo.
|
|
65
68
|
- Iniciar con Windows: el interruptor «Iniciar con Windows», en la misma tarjeta, está apagado hasta que lo enciendas. Encendido, el programa empieza solo al iniciar sesión en Windows, solo para tu usuario y sin abrir el navegador ni la página.
|
|
66
69
|
- Actividad: la tarjeta «Actividad» de la página lista lo que el programa hizo desde que empezó: el cliente del juego encontrado y perdido, cada fase, la partida aceptada y el aviso enviado. Guarda las últimas 50 líneas, solo en memoria.
|
|
@@ -111,11 +114,13 @@ If Windows answers that `python` was not found, use `py` in its place: `py -m pi
|
|
|
111
114
|
|
|
112
115
|
This way leaves no shortcut on the Desktop or in the Start menu, and the start with Windows is the switch on the page. To update, press «Quit» on the page, run the same pip line again and start it again. To remove it, first turn off the «Start with Windows» switch on the page, press «Quit», then run `python -m pip uninstall pendify`. The configuration in `%APPDATA%\pendify`, with the link to the phone, stays; you can delete that folder by hand.
|
|
113
116
|
|
|
117
|
+
The program checks PyPI every six hours and, when a newer version exists, installs it in the background. The page then offers «Restart now» to start using it. To turn this off, start it with `--no-update` or write `"update": "off"` in `%APPDATA%\pendify\config.json`. Automatic updates install for your user (--user).
|
|
118
|
+
|
|
114
119
|
### Link
|
|
115
120
|
|
|
116
121
|
When it starts, the browser opens a local page with a QR code. Scan it with the phone's camera: the Pendi app opens and asks you to confirm the link. The page shows when the link is done.
|
|
117
122
|
|
|
118
|
-
The QR code and the key show for a minute when «Show the code» is pressed, and
|
|
123
|
+
The QR code and the key are hidden; they show for a minute when «Show the code» is pressed, and the page says what the code is for.
|
|
119
124
|
|
|
120
125
|
The button at the top right switches between the light and the dark theme, and the program keeps the choice for the next time.
|
|
121
126
|
|
|
@@ -123,6 +128,7 @@ The button at the top right switches between the light and the dark theme, and t
|
|
|
123
128
|
|
|
124
129
|
- Start: the `PendiFy` shortcut, or `pythonw -m pendify`, which is what the shortcut runs; with no console, it tells you in a window when it is already running or cannot start. To see it in a console: `python -m pendify`.
|
|
125
130
|
- Stop: press «Quit» on the program's page; if it is not open, starting the program again opens it. As a last resort, end the `pythonw.exe` process in Task Manager.
|
|
131
|
+
- Icon by the clock: a click on the PendiFy icon by the clock opens a menu that opens the page, pauses or resumes the alerts and quits the program, also with the page and the browser closed.
|
|
126
132
|
- Pause: «Pause alerts», on the page's «This PC» card, stops reading the game client: it accepts no match and sends no alert to your phone, and the page stays open. «Resume alerts» reads it again. The pause is not kept: every start of the program is active again.
|
|
127
133
|
- Start with Windows: the «Start with Windows» switch, on the same card, is off until you turn it on. When it is on, the program starts by itself when you sign in to Windows, for your user only, without opening the browser or the page.
|
|
128
134
|
- Activity: the page's «Activity» card lists what the program did since it started: the game client found and lost, each phase, the match accepted and the alert sent. It keeps the last 50 lines, in memory only.
|
|
@@ -33,11 +33,13 @@ Si Windows responde que no encuentra `python`, usa `py` en su lugar: `py -m pip
|
|
|
33
33
|
|
|
34
34
|
Así no se crean los accesos directos en el Escritorio ni en el menú Inicio, y el inicio con Windows queda en el interruptor de la página. Para actualizar, pulsa «Salir» en la página, ejecuta otra vez la misma línea de pip y vuelve a iniciarlo. Para quitarlo, apaga primero el interruptor «Iniciar con Windows» en la página, pulsa «Salir» y después ejecuta `python -m pip uninstall pendify`. La configuración en `%APPDATA%\pendify`, con el enlace con el teléfono, se queda; puedes borrar esa carpeta a mano.
|
|
35
35
|
|
|
36
|
+
El programa revisa PyPI cada seis horas y, cuando hay una versión más nueva, la instala en segundo plano. La página ofrece entonces «Reiniciar ahora» para empezar a usarla. Para apagarlo, inícialo con `--no-update` o escribe `"update": "off"` en `%APPDATA%\pendify\config.json`. Las actualizaciones automáticas se instalan para tu usuario (--user).
|
|
37
|
+
|
|
36
38
|
### Enlazar
|
|
37
39
|
|
|
38
40
|
Al iniciarse, el navegador abre una página local con un código QR. Escanéalo con la cámara del teléfono: se abre la app Pendi y te pide confirmar el enlace. La página muestra cuando el enlace quedó hecho.
|
|
39
41
|
|
|
40
|
-
El código QR y la clave se muestran durante un minuto al pulsar «Mostrar el código
|
|
42
|
+
El código QR y la clave están ocultos; se muestran durante un minuto al pulsar «Mostrar el código», y la página dice para qué sirve el código.
|
|
41
43
|
|
|
42
44
|
El botón de arriba a la derecha cambia entre el tema claro y el oscuro, y el programa guarda la elección para la próxima vez.
|
|
43
45
|
|
|
@@ -45,6 +47,7 @@ El botón de arriba a la derecha cambia entre el tema claro y el oscuro, y el pr
|
|
|
45
47
|
|
|
46
48
|
- Iniciar: el acceso directo `PendiFy`, o `pythonw -m pendify`, que es lo que ejecuta el acceso directo; sin consola, si ya está abierto o no puede iniciarse te lo dice en una ventana. Para verlo en una consola: `python -m pendify`.
|
|
47
49
|
- Detener: pulsa «Salir» en la página del programa; si no la tienes abierta, iniciarlo otra vez la abre. Como último recurso, cierra el proceso `pythonw.exe` en el Administrador de tareas.
|
|
50
|
+
- Icono junto al reloj: un clic en el icono de PendiFy junto al reloj abre un menú para abrir la página, pausar o reanudar los avisos y salir del programa, también con la página y el navegador cerrados.
|
|
48
51
|
- Pausar: «Pausar avisos», en la tarjeta «Este PC» de la página, deja de leer el cliente del juego: no acepta partidas ni avisa a tu teléfono, y la página sigue abierta. «Reanudar avisos» vuelve a leerlo. La pausa no se guarda: cada vez que el programa se inicia, empieza activo.
|
|
49
52
|
- Iniciar con Windows: el interruptor «Iniciar con Windows», en la misma tarjeta, está apagado hasta que lo enciendas. Encendido, el programa empieza solo al iniciar sesión en Windows, solo para tu usuario y sin abrir el navegador ni la página.
|
|
50
53
|
- Actividad: la tarjeta «Actividad» de la página lista lo que el programa hizo desde que empezó: el cliente del juego encontrado y perdido, cada fase, la partida aceptada y el aviso enviado. Guarda las últimas 50 líneas, solo en memoria.
|
|
@@ -95,11 +98,13 @@ If Windows answers that `python` was not found, use `py` in its place: `py -m pi
|
|
|
95
98
|
|
|
96
99
|
This way leaves no shortcut on the Desktop or in the Start menu, and the start with Windows is the switch on the page. To update, press «Quit» on the page, run the same pip line again and start it again. To remove it, first turn off the «Start with Windows» switch on the page, press «Quit», then run `python -m pip uninstall pendify`. The configuration in `%APPDATA%\pendify`, with the link to the phone, stays; you can delete that folder by hand.
|
|
97
100
|
|
|
101
|
+
The program checks PyPI every six hours and, when a newer version exists, installs it in the background. The page then offers «Restart now» to start using it. To turn this off, start it with `--no-update` or write `"update": "off"` in `%APPDATA%\pendify\config.json`. Automatic updates install for your user (--user).
|
|
102
|
+
|
|
98
103
|
### Link
|
|
99
104
|
|
|
100
105
|
When it starts, the browser opens a local page with a QR code. Scan it with the phone's camera: the Pendi app opens and asks you to confirm the link. The page shows when the link is done.
|
|
101
106
|
|
|
102
|
-
The QR code and the key show for a minute when «Show the code» is pressed, and
|
|
107
|
+
The QR code and the key are hidden; they show for a minute when «Show the code» is pressed, and the page says what the code is for.
|
|
103
108
|
|
|
104
109
|
The button at the top right switches between the light and the dark theme, and the program keeps the choice for the next time.
|
|
105
110
|
|
|
@@ -107,6 +112,7 @@ The button at the top right switches between the light and the dark theme, and t
|
|
|
107
112
|
|
|
108
113
|
- Start: the `PendiFy` shortcut, or `pythonw -m pendify`, which is what the shortcut runs; with no console, it tells you in a window when it is already running or cannot start. To see it in a console: `python -m pendify`.
|
|
109
114
|
- Stop: press «Quit» on the program's page; if it is not open, starting the program again opens it. As a last resort, end the `pythonw.exe` process in Task Manager.
|
|
115
|
+
- Icon by the clock: a click on the PendiFy icon by the clock opens a menu that opens the page, pauses or resumes the alerts and quits the program, also with the page and the browser closed.
|
|
110
116
|
- Pause: «Pause alerts», on the page's «This PC» card, stops reading the game client: it accepts no match and sends no alert to your phone, and the page stays open. «Resume alerts» reads it again. The pause is not kept: every start of the program is active again.
|
|
111
117
|
- Start with Windows: the «Start with Windows» switch, on the same card, is off until you turn it on. When it is on, the program starts by itself when you sign in to Windows, for your user only, without opening the browser or the page.
|
|
112
118
|
- Activity: the page's «Activity» card lists what the program did since it started: the game client found and lost, each phase, the match accepted and the alert sent. It keeps the last 50 lines, in memory only.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pendify"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.5"
|
|
8
8
|
description = "Pairs this PC with a phone account by a QR on a loopback page and sends it two kinds of alerts."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -4,11 +4,15 @@
|
|
|
4
4
|
python -m <package> --dry the same, but the watcher never accepts: it alerts at the queue pop
|
|
5
5
|
python -m <package> --quiet the same, started by the system at logon: no browser, and no message box
|
|
6
6
|
unless the start fails
|
|
7
|
+
python -m <package> --no-update the same, with no check of PyPI for a newer version (update.py)
|
|
7
8
|
python -m <package> ping <kind> one alert with the stored pair, one fixed line per answer
|
|
8
9
|
|
|
9
|
-
The page and the watcher stop together on Ctrl+C, on Ctrl+Break
|
|
10
|
-
|
|
11
|
-
and exits 0, and one made while that program is still closing after its quit waits for
|
|
10
|
+
The page and the watcher stop together on Ctrl+C, on Ctrl+Break, on the page's quit button and on the quit of
|
|
11
|
+
the icon by the clock, which remove the run file and exit 0. One instance per config folder: a second start
|
|
12
|
+
opens the running page and exits 0, and one made while that program is still closing after its quit waits for
|
|
13
|
+
it to end and starts. A restart the page asks for («Reiniciar ahora», once a newer version is installed, or the
|
|
14
|
+
icon's «Reiniciar para actualizar») starts a new copy once the run file is released: without --quiet, so its page
|
|
15
|
+
opens, and with the --dry, --data-dir, --worker and --client-lockfile of this one.
|
|
12
16
|
Where no console is attached (a pythonw start), the line a start ends on is also shown in a
|
|
13
17
|
message box. `--data-dir`, `--worker` and `--client-lockfile` are the
|
|
14
18
|
test overrides, loopback only, so a test run reads neither the profile, the Worker nor the client's files.
|
|
@@ -16,14 +20,16 @@ test overrides, loopback only, so a test run reads neither the profile, the Work
|
|
|
16
20
|
import argparse
|
|
17
21
|
import os
|
|
18
22
|
import signal
|
|
23
|
+
import subprocess
|
|
19
24
|
import sys
|
|
20
25
|
import threading
|
|
21
26
|
import time
|
|
22
27
|
import webbrowser
|
|
23
28
|
from pathlib import Path
|
|
24
29
|
|
|
25
|
-
from . import alert, client, config, page, pairing, runfile, watcher, worker
|
|
30
|
+
from . import alert, client, config, page, pairing, runfile, update, watcher, worker
|
|
26
31
|
from .autostart import Autostart
|
|
32
|
+
from .tray import Tray
|
|
27
33
|
|
|
28
34
|
TICK_SECONDS = 1.0
|
|
29
35
|
STOP_SECONDS = 5.0
|
|
@@ -39,8 +45,14 @@ PAGE_NOT_KNOWN_LINE = ("already running: the page of the program that runs is no
|
|
|
39
45
|
"moment to open it")
|
|
40
46
|
CLAIM_FAILED_LINE = "cannot start: the run file cannot be replaced: {path}"
|
|
41
47
|
FOLDER_FAILED_LINE = "cannot start: the config folder cannot be written: {path}"
|
|
48
|
+
RESTART_FAILED_LINE = "cannot restart: the new copy did not start; start the program again"
|
|
42
49
|
_MB_ICONINFORMATION = 0x40
|
|
43
50
|
_MB_SETFOREGROUND = 0x10000
|
|
51
|
+
# The restart's new copy: no console, its own process group, out of this one's.
|
|
52
|
+
_DETACHED_PROCESS = 0x00000008
|
|
53
|
+
_CREATE_NEW_PROCESS_GROUP = 0x00000200
|
|
54
|
+
# The icon by the clock's picture: pendify.ico beside the modules, the file install.ps1 points the shortcuts at.
|
|
55
|
+
ICON_PATH = Path(__file__).with_name("pendify.ico")
|
|
44
56
|
|
|
45
57
|
|
|
46
58
|
def _data_dir(value):
|
|
@@ -78,6 +90,8 @@ def _arguments(argv):
|
|
|
78
90
|
parser.add_argument("--dry", action="store_true", help="watch and alert, but never accept")
|
|
79
91
|
parser.add_argument("--quiet", action="store_true",
|
|
80
92
|
help="a start by the system: no browser, and no message box for a start that ends well")
|
|
93
|
+
parser.add_argument("--no-update", action="store_true",
|
|
94
|
+
help="no check of PyPI for a newer version in this run")
|
|
81
95
|
commands = parser.add_subparsers(dest="command")
|
|
82
96
|
ping = commands.add_parser("ping", parents=[common], help="send one alert with the stored pair")
|
|
83
97
|
ping.add_argument("kind", choices=worker.KINDS)
|
|
@@ -150,7 +164,31 @@ def _ends(line, code, show):
|
|
|
150
164
|
return code
|
|
151
165
|
|
|
152
166
|
|
|
153
|
-
def
|
|
167
|
+
def _restart(args, spawn, show, opens_page):
|
|
168
|
+
"""The restart asked for, once the run file is free: a new copy of this program, detached and with no console,
|
|
169
|
+
which claims the run file as a first start does. One that opens its page, as every restart asked today does,
|
|
170
|
+
goes without --quiet; one that does not keeps this run's --quiet. This run's --dry, --data-dir, --worker and
|
|
171
|
+
--client-lockfile go with it either way."""
|
|
172
|
+
command = [sys.executable, "-m", __package__] + (["--quiet"] if args.quiet and not opens_page else [])
|
|
173
|
+
command += ["--dry"] if args.dry else []
|
|
174
|
+
for option in ("data_dir", "worker", "client_lockfile"):
|
|
175
|
+
if hasattr(args, option): # the parser's SUPPRESS: present only when this run was given it
|
|
176
|
+
command += ["--" + option.replace("_", "-"), str(getattr(args, option))]
|
|
177
|
+
try:
|
|
178
|
+
spawn(command, creationflags=_DETACHED_PROCESS | _CREATE_NEW_PROCESS_GROUP, close_fds=True)
|
|
179
|
+
except OSError:
|
|
180
|
+
return _ends(RESTART_FAILED_LINE, 1, show)
|
|
181
|
+
return 0
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _ready_version(updater):
|
|
185
|
+
"""The version an install made ready, for the icon's item; None in any other state."""
|
|
186
|
+
seen = updater.snapshot()
|
|
187
|
+
return seen["version"] if seen["state"] == update.READY else None
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _serve(args, store, base, timeout, opener, stop, delay, beep, show, clock, sleep, autostart, tray, updater,
|
|
191
|
+
spawn):
|
|
154
192
|
# A quiet start is the system's, at logon: it never opens the browser, and a start that ends well shows no box.
|
|
155
193
|
calm = (lambda line: None) if args.quiet else show
|
|
156
194
|
run = runfile.RunFile(store.path.parent, clock=clock, sleep=sleep)
|
|
@@ -175,7 +213,9 @@ def _serve(args, store, base, timeout, opener, stop, delay, beep, show, clock, s
|
|
|
175
213
|
opener(f"http://{page.ADDRESS}:{port}/")
|
|
176
214
|
show(ALREADY_RUNNING_LINE)
|
|
177
215
|
return 0
|
|
216
|
+
updating = None
|
|
178
217
|
try:
|
|
218
|
+
mode = update.OFF if args.no_update else store.read_update()
|
|
179
219
|
state = pairing.PairingState(store, lambda secret: worker.check(secret, base=base, timeout=timeout))
|
|
180
220
|
watch, alerter = _watcher(args, store, state, base, timeout, stop, delay, beep)
|
|
181
221
|
|
|
@@ -192,8 +232,22 @@ def _serve(args, store, base, timeout, opener, stop, delay, beep, show, clock, s
|
|
|
192
232
|
pairing_page = page.PairingPage(state, watch=watch.snapshot, on_quit=quit_page, on_pause=watch.pause,
|
|
193
233
|
on_resume=watch.resume, autostart=autostart, events=watch.events)
|
|
194
234
|
url = pairing_page.start()
|
|
235
|
+
# The update's rounds on their own thread (update.py), built once the page serves; its restart is the
|
|
236
|
+
# page's quit, after the flag read below once the run file is free; pip's output goes to the config folder.
|
|
237
|
+
updating = updater(update.RUNNING_VERSION, mode, restart=quit_page, folder=store.path.parent)
|
|
238
|
+
pairing_page.updater = updating
|
|
239
|
+
# The icon by the clock, under --quiet as well: the way to stop a copy the Run key started. Its words are
|
|
240
|
+
# the page's, in the language the page resolves with no browser to ask: the kept choice, else Spanish.
|
|
241
|
+
icon = tray(url, on_open=lambda: opener(url), on_pause=watch.pause, on_resume=watch.resume,
|
|
242
|
+
on_quit=quit_page, paused=lambda: watch.paused,
|
|
243
|
+
words=lambda: page.WORDS[pairing_page.language_of(None)], icon_path=ICON_PATH,
|
|
244
|
+
# «Reiniciar para actualizar a X» once an install is ready, the page's own restart
|
|
245
|
+
update_ready=lambda: _ready_version(updating), on_restart=updating.restart)
|
|
195
246
|
watching = threading.Thread(target=watch.run, daemon=True)
|
|
196
247
|
try:
|
|
248
|
+
icon.start()
|
|
249
|
+
if mode != update.OFF:
|
|
250
|
+
updating.start()
|
|
197
251
|
run.publish(pairing_page.port)
|
|
198
252
|
print(f"page: {url}", flush=True)
|
|
199
253
|
if not args.quiet and not opener(url): # a start by a person opens the page, linked or not
|
|
@@ -207,6 +261,7 @@ def _serve(args, store, base, timeout, opener, stop, delay, beep, show, clock, s
|
|
|
207
261
|
pass
|
|
208
262
|
finally:
|
|
209
263
|
stop.set()
|
|
264
|
+
updating.close() # the restart gate shuts at the quit
|
|
210
265
|
# First, so a start made while this one stops waits for it instead of opening a closing page: Ctrl+C
|
|
211
266
|
# and Ctrl+Break are marked only here, and the page's quit, marked already, is marked again.
|
|
212
267
|
try:
|
|
@@ -217,8 +272,14 @@ def _serve(args, store, base, timeout, opener, stop, delay, beep, show, clock, s
|
|
|
217
272
|
watching.join(STOP_SECONDS)
|
|
218
273
|
alerter.flush(STOP_SECONDS)
|
|
219
274
|
pairing_page.close()
|
|
275
|
+
icon.close()
|
|
276
|
+
# An install under way is waited for before the run file goes, so a quit does not end this copy while
|
|
277
|
+
# pip swaps the files; past the bound pip goes on alone, its output in a file.
|
|
278
|
+
updating.wait(update.INSTALL_JOIN_SECONDS)
|
|
220
279
|
finally:
|
|
221
280
|
run.release()
|
|
281
|
+
if updating is not None and updating.restart_requested.is_set():
|
|
282
|
+
return _restart(args, spawn, show, updating.restart_opens_page)
|
|
222
283
|
return 0
|
|
223
284
|
|
|
224
285
|
|
|
@@ -231,10 +292,13 @@ def _break_as_interrupt():
|
|
|
231
292
|
|
|
232
293
|
def main(argv=None, *, opener=webbrowser.open, stop=None, timeout=worker.TIMEOUT_SECONDS, delay=None,
|
|
233
294
|
beep=alert.beep, box=_message_box, autostart=None, console=_console_attached, clock=time.monotonic,
|
|
234
|
-
sleep=time.sleep):
|
|
295
|
+
sleep=time.sleep, tray=Tray, updater=update.Updater, spawn=subprocess.Popen):
|
|
235
296
|
"""`beep`, `box` and `autostart` are the seams of the sound, of the message box and of the start with Windows
|
|
236
297
|
(the per-user Run value when None): a test run passes silent ones and a fake. `console` says whether a printed
|
|
237
|
-
line reaches anybody; `clock` and `sleep` time the run file's re-reads.
|
|
298
|
+
line reaches anybody; `clock` and `sleep` time the run file's re-reads. `tray` makes the icon by the clock, the
|
|
299
|
+
seam a test run fills with a fake, so no test shows a real icon. `updater` makes the update and `spawn` starts
|
|
300
|
+
the restart's new copy, the seams a test run fills with fakes, so no test reaches PyPI, runs pip or starts a
|
|
301
|
+
copy."""
|
|
238
302
|
args = _arguments(sys.argv[1:] if argv is None else argv)
|
|
239
303
|
store = config.ConfigStore(getattr(args, "data_dir", None) or config.default_base_dir())
|
|
240
304
|
base = getattr(args, "worker", worker.BASE_URL)
|
|
@@ -250,7 +314,7 @@ def main(argv=None, *, opener=webbrowser.open, stop=None, timeout=worker.TIMEOUT
|
|
|
250
314
|
try:
|
|
251
315
|
return _serve(args, store, base, timeout, opener, stop or threading.Event(),
|
|
252
316
|
delay or watcher.accept_delay, beep, show, clock, sleep,
|
|
253
|
-
autostart if autostart is not None else Autostart())
|
|
317
|
+
autostart if autostart is not None else Autostart(), tray, updater, spawn)
|
|
254
318
|
finally:
|
|
255
319
|
if previous is not None:
|
|
256
320
|
signal.signal(signal.SIGBREAK, previous)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""The config file: the secret and the link id, under the user's profile (design P1, P6), and the page's theme
|
|
2
2
|
choice when there is one (light or dark; the system's choice is no key), and its language choice when there is one
|
|
3
|
-
(es or en; with none the page follows the browser)
|
|
3
|
+
(es or en; with none the page follows the browser), and the update's mode when one is written there by hand
|
|
4
|
+
(auto, notify or off; with none, auto).
|
|
4
5
|
|
|
5
6
|
The file is `<base>/<folder>/config.json`, where the base is %APPDATA% in a real run and is injected
|
|
6
7
|
everywhere else, so no test touches the real profile. A write goes to a temp file in the same folder
|
|
@@ -30,6 +31,10 @@ THEMES = ("light", "dark")
|
|
|
30
31
|
SYSTEM_THEME = "system"
|
|
31
32
|
# The language switch's choices; with no key the page follows the browser's language.
|
|
32
33
|
LANGS = ("es", "en")
|
|
34
|
+
# The update's modes (update.py), written by hand as the key "update": auto checks PyPI and installs in the
|
|
35
|
+
# background, notify checks only, off never checks. With no key, or a foreign value, auto.
|
|
36
|
+
UPDATE_MODES = ("auto", "notify", "off")
|
|
37
|
+
DEFAULT_UPDATE = "auto"
|
|
33
38
|
# _write's default: the stored theme and language choices are kept.
|
|
34
39
|
_KEEP = object()
|
|
35
40
|
|
|
@@ -92,6 +97,11 @@ class ConfigStore:
|
|
|
92
97
|
and cannot be read raises ConfigError, as read_theme() does; a missing, corrupt or foreign value is None."""
|
|
93
98
|
return _choice(self._stored(), "lang", LANGS)
|
|
94
99
|
|
|
100
|
+
def read_update(self):
|
|
101
|
+
"""The update's mode, auto, notify or off; auto when the file holds none or a foreign one. A file that is
|
|
102
|
+
there and cannot be read raises ConfigError, as read_theme() does."""
|
|
103
|
+
return _choice(self._stored(), "update", UPDATE_MODES) or DEFAULT_UPDATE
|
|
104
|
+
|
|
95
105
|
def set_theme(self, choice):
|
|
96
106
|
"""The theme button's choice written beside the pair: light or dark kept, system kept as no key. Any
|
|
97
107
|
other value is refused and nothing is written. Answers the stored choice, None for the system's."""
|
|
@@ -176,17 +186,19 @@ class ConfigStore:
|
|
|
176
186
|
|
|
177
187
|
def _write(self, pairing, theme=_KEEP, lang=_KEEP):
|
|
178
188
|
"""The pair, and the theme and language choices: each stored one kept unless set_theme or set_lang passes
|
|
179
|
-
its own."""
|
|
189
|
+
its own. The update's mode, written by hand, is kept as it is stored."""
|
|
180
190
|
folder = self.path.parent
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
191
|
+
stored = self._stored()
|
|
192
|
+
theme = _choice(stored, "theme", THEMES) if theme is _KEEP else theme
|
|
193
|
+
lang = _choice(stored, "lang", LANGS) if lang is _KEEP else lang
|
|
194
|
+
update = _choice(stored, "update", UPDATE_MODES)
|
|
185
195
|
values = {"secret": pairing.secret, "linkId": pairing.link_id}
|
|
186
196
|
if theme is not None:
|
|
187
197
|
values["theme"] = theme
|
|
188
198
|
if lang is not None:
|
|
189
199
|
values["lang"] = lang
|
|
200
|
+
if update is not None:
|
|
201
|
+
values["update"] = update
|
|
190
202
|
data = json.dumps(values).encode("utf-8")
|
|
191
203
|
try:
|
|
192
204
|
folder.mkdir(parents=True, exist_ok=True)
|