Was es ist

PyMini ist ein in Swift geschriebener Interpreter für eine Teilmenge von Python, fest in der Tastatur. Jedes eigene Panel und jede eigene Aktion läuft darauf. Er liest das Python, das du schon kennst, und dieselbe Sprache trägt beides: ein Skript, das eine Oberfläche zeichnet, und eines, das ein Wort umschreibt.

# an action: text in, text out
def transform(text):
    return text.upper()

# a panel: a view, and what a tap does
def view(state):
    return button("Shout", "shout")

def on_action(action, state):
    insert(context()["word"].upper())
    return state
Eine Sprache, zwei Verträge: transform(text) für eine Aktion, view(state) für ein Panel.

Warum PyMini

Wir haben PyMini gebaut, weil Clink eine Möglichkeit brauchte, die eigene Tastatur mit Skripten zu erweitern, und nichts, was wir fanden, in eine Tastatur passte. An Tastaturen gebunden ist daran aber nichts. Deshalb ist es jetzt Open Source und ein eigenständiges Swift-Paket, für jede App, deren Nutzer ein wenig Python schreiben sollen.

Es ist nicht CPython und bettet es auch nicht ein. Eine Tastaturerweiterung bekommt einen Bruchteil des Speichers einer App, und CPythons Binärdatei und Heap wären aufgebraucht, bevor dein Skript liefe. PyMini sind ein paar hundert Kilobyte Swift, ganz ohne Binärabhängigkeiten.

Es passt überall dort, wo Nutzer eigene Logik brauchen: Textumformungen in einer Schreib-App, Regeln in einer To-do- oder Budget-App, Formeln, Automationen, Spielmods oder ein Plugin-System für eine App mit eigener Community. Viele Menschen können schon ein bisschen Python, du musst also keine neue Sprache beibringen, und ein Skript ist einfacher Text, den du wie jede andere Einstellung speichern, synchronisieren und teilen kannst.

import PyMini

let py = PyRuntime(modules: ["math", "json", "re"])

// your app, as a Python function
py.define("shout") { call in
    try call.argument(0, as: String.self).uppercased()
}

// a script one of your users wrote
try py.load("""
def tidy(text):
    return shout(" ".join(text.split()))
""")

try py.call("tidy", "  hello    there ")  // HELLO THERE
Gib Skripten eine Swift-Funktion, lade ein Skript und rufe es auf.
AufrufWas er tut
import PyMiniKeine Abhängigkeiten, nichts, was du mitliefern musst. Füge das Swift-Paket hinzu, und es läuft ab iOS 15, macOS 12, tvOS 15, watchOS 8 und visionOS 1.
py.define("shout") { call in ... }Die Funktionen deiner App werden zu Python-Funktionen und -Modulen. Argumente kommen als Swift-Typen an, und was du zurückgibst, erhält das Skript als Python-Wert.
PyLimits(steps: 200_000)Jeder Aufruf läuft mit einem Schrittbudget, und wenn es aufgebraucht ist, entsteht ein Fehler, den kein Skript abfangen kann. Ein unbedachtes while True endet mit einer Meldung statt mit einer eingefrorenen App.
PyRuntime(modules: ["math"])Skripte bekommen keine Dateien, kein Netzwerk und keine Prozesse. Sie erreichen nur die Funktionen, die du definierst, und die Standardmodule, die du eingeschaltet lässt.
py.call(hook: "on_open")Rufe ein Skript auf, wenn in deiner App etwas passiert. Ein Hook, den das Skript nie definiert hat, wird übersprungen, also kann jeder Hook optional sein.
catch let error as PyErrorFehler kommen mit dem Namen der Python-Exception und einer Zeilennummer zurück, bereit, sie der Person zu zeigen, die das Skript geschrieben hat.
import syssys.version nennt den Interpreter und die Version, und der Befehl pymini gibt dieselbe Zeile aus. So kann ein Skript melden, welchen Build die App um es herum mitbringt.

Quellcode, Sprachreferenz und Tests liegen auf GitHub.

Was es zusichert

Es gibt kein Dateisystem, kein Netzwerk und keinen Prozess, an den ein Skript herankäme: Die Sandbox ist die Form des Interpreters, keine Liste von Dingen, die er ablehnt. import ist die einzige Tür nach draußen, und sie führt auf eine kurze Liste von Namensräumen, die nur rechnen. Alles andere kostet Schritte. Jeder Ausdruck, jeder Schleifendurchlauf, jeder Schritt eines regulären Ausdrucks und jede Zeile JSON verbraucht einen von 2.000.000, und Zeichenketten und Listen sind im Wachstum gedeckelt.

In einem geteilten Panel importierbar

  • json
  • math
  • random
  • re
  • sys
  • time

Das Budget lässt sich nicht abfangen. Eine except-Klausel fängt einen KeyError, aber nicht den Abbruch, der eine entlaufene Schleife beendet: Ihn abzufangen wäre genau der Weg, auf dem ein entlaufenes Skript weiterliefe.

Änderungsprotokoll

Jede Version von PyMini, die neueste zuerst, und was sie hinzugefügt hat.

PyMini 2.0

Die erste Version konnte ein Wort großschreiben und ein Raster aus Schaltflächen anordnen. Die zweite ist eine Sprache, in der du ein kleines Programm schreiben kannst, und ein Panel, das auf eine Berührung antwortet, statt nur eine feste mit sich zu tragen.

BeispielWas neu ist
[word for word in words if word]Comprehensions, Lambdas, Tupel, Mengen sowie try / except / finally / raise.
f"{total:,.2f}"f-String-Konvertierungen und vollständige Formatangaben, gemeinsam mit str.format und format().
f"""Hello {name}"""Zeichenketten in dreifachen Anführungszeichen, Raw-Strings und f-Strings über mehrere Zeilen.
import math, random, time, json, reFünf Namensräume und sonst keine, jeder davon rein rechnend.
re.sub(r"\s+", " ", text)Reguläre Ausdrücke auf Clinks eigener Engine, die dasselbe Schrittbudget verbraucht.
on_action(action, value, state)Panels behandeln ihre Berührungen selbst, halten echten Zustand und haben elf Bedienelemente mehr.
replace(len(word), word.upper())Skripte können die Tastatur um eine Handlung bitten und lesen, was rund um den Cursor steht.
Current sentenceAktionen lesen jeden Ausschnitt des Feldes, nicht nur das Wort und was davor steht.
import re

def initial():
    return {"words": []}

def view(state):
    top = sorted(set(state["words"]), key=lambda w: -len(w))[:3]
    return vstack([text(f"{len(state['words']):,} words", mono=True)]
                  + [row(w, detail=f"{len(w)}") for w in top])

def on_open(state):
    state["words"] = re.findall(r"\w+", context()["before"])
    return state
Ein Panel, das liest, was du geschrieben hast, es zählt und die längsten Wörter auflistet.

PyMini 1.0

Die erste Version. Eine Aktion machte aus einer Zeichenkette eine andere, und ein Panel bestand aus ein paar Bedienelementen, deren Schaltflächen einen festen Text tippten oder einen Wert setzten.

BeispielWas neu ist
def transform(text):int, float, str, bool, None, list und dict, dazu if, while, for und def mit Standard- und Schlüsselwortargumenten.
f"{n} words"f-Strings ohne Formatangaben sowie Indizierung und Slicing mit negativen Indizes.
sorted(text.split())24 eingebaute Funktionen und die gängigen Methoden von str, list und dict.
button("+", set={"count": 1})Text, Schaltflächen, Felder, Stapel und ein Raster. Eine Schaltfläche konnte Text tippen oder Zustand setzen, mehr nicht.
while True:Überhaupt keine Imports, ein Schrittbudget für jedes Skript und Obergrenzen dafür, wie weit Zeichenketten und Listen wachsen dürfen.

Wie es weitergeht

Die Sprache in voller Breite, das Panel-Vokabular und ein Baukasten, der beides schreibt, ohne dass du eine Zeile tippst.

Aktionen ›Bereiche ›Plugins ›Baukasten ›
Im App Store laden