shortcutkit 3.0.0__tar.gz → 3.1.0__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.
- {shortcutkit-3.0.0/src/shortcutkit.egg-info → shortcutkit-3.1.0}/PKG-INFO +10 -4
- {shortcutkit-3.0.0 → shortcutkit-3.1.0}/README.md +9 -3
- {shortcutkit-3.0.0 → shortcutkit-3.1.0}/pyproject.toml +1 -1
- {shortcutkit-3.0.0 → shortcutkit-3.1.0}/src/shortcutkit/__init__.py +63 -17
- {shortcutkit-3.0.0 → shortcutkit-3.1.0}/src/shortcutkit/actions.py +2324 -1714
- {shortcutkit-3.0.0 → shortcutkit-3.1.0}/src/shortcutkit/entities.py +102 -0
- {shortcutkit-3.0.0 → shortcutkit-3.1.0/src/shortcutkit.egg-info}/PKG-INFO +10 -4
- {shortcutkit-3.0.0 → shortcutkit-3.1.0}/LICENSE +0 -0
- {shortcutkit-3.0.0 → shortcutkit-3.1.0}/setup.cfg +0 -0
- {shortcutkit-3.0.0 → shortcutkit-3.1.0}/src/shortcutkit/__main__.py +0 -0
- {shortcutkit-3.0.0 → shortcutkit-3.1.0}/src/shortcutkit/data/builtin-actions.json +0 -0
- {shortcutkit-3.0.0 → shortcutkit-3.1.0}/src/shortcutkit/data/provenance.json +0 -0
- {shortcutkit-3.0.0 → shortcutkit-3.1.0}/src/shortcutkit.egg-info/SOURCES.txt +0 -0
- {shortcutkit-3.0.0 → shortcutkit-3.1.0}/src/shortcutkit.egg-info/dependency_links.txt +0 -0
- {shortcutkit-3.0.0 → shortcutkit-3.1.0}/src/shortcutkit.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shortcutkit
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.1.0
|
|
4
4
|
Summary: Build, validate, and sign Apple Shortcuts files from Python. All 449 built-in actions and 1,265 Apple app actions, extracted from the Shortcuts engine.
|
|
5
5
|
Author: frontboat
|
|
6
6
|
License-Expression: MIT
|
|
@@ -118,7 +118,12 @@ s.action(actions.SHOWRESULT, Text=text(property_(found, "body")))
|
|
|
118
118
|
|
|
119
119
|
A dictionary parameter takes a plain dict: strings, `text()` and references are text items, and
|
|
120
120
|
numbers, booleans, lists and nested dicts keep their type, as the editor's item type menu sets it.
|
|
121
|
-
`coerce()` reads a value as one content type, as the variable "Type" menu does
|
|
121
|
+
`coerce()` reads a value as one content type, as the variable "Type" menu does, and `property_()` on the
|
|
122
|
+
result takes that class's properties, the menu's "Get" list (`CONTENT_ITEM_PROPERTIES`):
|
|
123
|
+
`property_(coerce(file, "WFGenericFileContentItem"), "File Size")`, checked at run time. An output the catalogue types, by
|
|
124
|
+
class or by the object class it wraps (`CONTENT_ITEM_TYPES`), is checked the same way without a coercion:
|
|
125
|
+
`property_(asked_number, "Name")`. Where a choice decides the output, the literal you pass narrows it:
|
|
126
|
+
`s.action(actions.ASK, WFInputType="Number")` outputs a Number, so only Name is accepted.
|
|
122
127
|
|
|
123
128
|
```python
|
|
124
129
|
from shortcutkit import coerce
|
|
@@ -162,11 +167,12 @@ s.action(actions.SHOWRESULT, Text=ref(note))
|
|
|
162
167
|
| `shortcut_input()`, `clipboard()`, `current_date()`, `ask()`, `device_details()`, `current_app()` | Special variables. |
|
|
163
168
|
| `where(*rows)`, `where_any(*rows)` | Conditions for a Find action's `WFContentItemFilter`. |
|
|
164
169
|
| `property_(source, name)` | One property of an action's output or a variable. |
|
|
165
|
-
| `coerce(source, class)` | A value read as one content type (`CONTENT_ITEM_CLASSES`). |
|
|
170
|
+
| `coerce(source, class)` | A value read as one content type, the editor's "Type" menu (`CONTENT_ITEM_CLASSES`). |
|
|
166
171
|
| `dictionary(items)` | A dictionary parameter's items; `action()` encodes a plain dict itself. |
|
|
167
172
|
| `app_intent(app, intent, output=None)` | Another app's action, for `s.action()`. |
|
|
168
173
|
| `FILTERS`, `ENTITY_PROPERTIES`, `FILTER_OPERATORS`, `CALENDAR_UNITS`, `CONTENT_ITEM_CLASSES` | What each Find action can filter on, each entity type's properties, and the content types a coercion can name. |
|
|
169
|
-
| `actions`, `ACTIONS`, `get_action(id)` | Action identifiers and their catalog entries. |
|
|
174
|
+
| `actions`, `ACTIONS`, `get_action(id)` | Action identifiers and their catalog entries: parameters and kinds, output and input types, keywords, description, flags, and the rules under which the editor shows each parameter. |
|
|
175
|
+
| `accepted_by(type)`, `produced_by(type)` | Which actions take a content type as input or as an entity parameter, and which output it. |
|
|
170
176
|
| `PARAM_KINDS`, `PARAM_CHOICES`, `PARAM_VARIABLE_TYPES` | What each parameter accepts. |
|
|
171
177
|
| `CONDITION`, `ICON_COLORS`, `PROVENANCE` | If conditions, icon colors, and the build the data came from. |
|
|
172
178
|
|
|
@@ -97,7 +97,12 @@ s.action(actions.SHOWRESULT, Text=text(property_(found, "body")))
|
|
|
97
97
|
|
|
98
98
|
A dictionary parameter takes a plain dict: strings, `text()` and references are text items, and
|
|
99
99
|
numbers, booleans, lists and nested dicts keep their type, as the editor's item type menu sets it.
|
|
100
|
-
`coerce()` reads a value as one content type, as the variable "Type" menu does
|
|
100
|
+
`coerce()` reads a value as one content type, as the variable "Type" menu does, and `property_()` on the
|
|
101
|
+
result takes that class's properties, the menu's "Get" list (`CONTENT_ITEM_PROPERTIES`):
|
|
102
|
+
`property_(coerce(file, "WFGenericFileContentItem"), "File Size")`, checked at run time. An output the catalogue types, by
|
|
103
|
+
class or by the object class it wraps (`CONTENT_ITEM_TYPES`), is checked the same way without a coercion:
|
|
104
|
+
`property_(asked_number, "Name")`. Where a choice decides the output, the literal you pass narrows it:
|
|
105
|
+
`s.action(actions.ASK, WFInputType="Number")` outputs a Number, so only Name is accepted.
|
|
101
106
|
|
|
102
107
|
```python
|
|
103
108
|
from shortcutkit import coerce
|
|
@@ -141,11 +146,12 @@ s.action(actions.SHOWRESULT, Text=ref(note))
|
|
|
141
146
|
| `shortcut_input()`, `clipboard()`, `current_date()`, `ask()`, `device_details()`, `current_app()` | Special variables. |
|
|
142
147
|
| `where(*rows)`, `where_any(*rows)` | Conditions for a Find action's `WFContentItemFilter`. |
|
|
143
148
|
| `property_(source, name)` | One property of an action's output or a variable. |
|
|
144
|
-
| `coerce(source, class)` | A value read as one content type (`CONTENT_ITEM_CLASSES`). |
|
|
149
|
+
| `coerce(source, class)` | A value read as one content type, the editor's "Type" menu (`CONTENT_ITEM_CLASSES`). |
|
|
145
150
|
| `dictionary(items)` | A dictionary parameter's items; `action()` encodes a plain dict itself. |
|
|
146
151
|
| `app_intent(app, intent, output=None)` | Another app's action, for `s.action()`. |
|
|
147
152
|
| `FILTERS`, `ENTITY_PROPERTIES`, `FILTER_OPERATORS`, `CALENDAR_UNITS`, `CONTENT_ITEM_CLASSES` | What each Find action can filter on, each entity type's properties, and the content types a coercion can name. |
|
|
148
|
-
| `actions`, `ACTIONS`, `get_action(id)` | Action identifiers and their catalog entries. |
|
|
153
|
+
| `actions`, `ACTIONS`, `get_action(id)` | Action identifiers and their catalog entries: parameters and kinds, output and input types, keywords, description, flags, and the rules under which the editor shows each parameter. |
|
|
154
|
+
| `accepted_by(type)`, `produced_by(type)` | Which actions take a content type as input or as an entity parameter, and which output it. |
|
|
149
155
|
| `PARAM_KINDS`, `PARAM_CHOICES`, `PARAM_VARIABLE_TYPES` | What each parameter accepts. |
|
|
150
156
|
| `CONDITION`, `ICON_COLORS`, `PROVENANCE` | If conditions, icon colors, and the build the data came from. |
|
|
151
157
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "shortcutkit"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.1.0"
|
|
8
8
|
description = "Build, validate, and sign Apple Shortcuts files from Python. All 449 built-in actions and 1,265 Apple app actions, extracted from the Shortcuts engine."
|
|
9
9
|
requires-python = ">=3.9"
|
|
10
10
|
readme = "README.md"
|
|
@@ -26,8 +26,8 @@ Value helpers return the exact structures WorkflowKit's state classes serialize:
|
|
|
26
26
|
ask("Prompt") WFTextTokenAttachment -> ask each time
|
|
27
27
|
text("a ", ref(x)) WFTextTokenString -> text with embedded references
|
|
28
28
|
subject(att) {"Type": "Variable", "Variable": att} (the If condition's subject; pickers take the bare attachment)
|
|
29
|
-
property_(x, "body") a reference to one property of x (a WFPropertyVariableAggrandizement)
|
|
30
29
|
coerce(x, "WFStringContentItem") x read as one content type (a WFCoercionVariableAggrandizement; CONTENT_ITEM_CLASSES)
|
|
30
|
+
property_(x, "body") one property of x (a WFPropertyVariableAggrandizement; ENTITY_PROPERTIES, CONTENT_ITEM_PROPERTIES)
|
|
31
31
|
dictionary({...}) WFDictionaryFieldValue -> a dictionary parameter's items, typed as the editor types them
|
|
32
32
|
app_intent(app, "CreateNoteIntent") another app's action, for action(): its identifier and descriptor
|
|
33
33
|
where(row, ...) Find conditions, encoded by action() for the Find action's entity
|
|
@@ -189,19 +189,38 @@ def subject(attachment):
|
|
|
189
189
|
|
|
190
190
|
|
|
191
191
|
def property_(source, name):
|
|
192
|
-
"""One property of an action's output or of any reference, as the editor's property picker
|
|
193
|
-
it (a WFPropertyVariableAggrandizement): property_(found, "body"),
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
192
|
+
"""One property of an action's output or of any reference, as the editor's property picker (the "Get" list
|
|
193
|
+
under a variable's "Type") writes it (a WFPropertyVariableAggrandizement): property_(found, "body"),
|
|
194
|
+
property_(repeat_item(), "date"), property_(coerce(file, "WFGenericFileContentItem"), "File Size").
|
|
195
|
+
When the output's type is known, one of Apple's entity types (ENTITY_PROPERTIES) or a content item class
|
|
196
|
+
(CONTENT_ITEM_PROPERTIES: named directly, or as the object class the definition names, NSDecimalNumber being a
|
|
197
|
+
Number, through CONTENT_ITEM_TYPES; also what a coerce()d reference has), the name must be one of its properties."""
|
|
198
|
+
if _is_reference(source):
|
|
199
|
+
cls = _coerced_class(source)
|
|
200
|
+
types = [cls] if cls else []
|
|
201
|
+
else:
|
|
202
|
+
declared = (source["_outputClasses"] + [t for t in source.get("_outputTypes", []) if t in ENTITY_PROPERTIES]) if source.get("_outputClasses") else source.get("_outputTypes", [])
|
|
203
|
+
types = list(dict.fromkeys(t if t in ENTITY_PROPERTIES else _item_class_of(t) for t in declared))
|
|
204
|
+
types = [t for t in types if t]
|
|
205
|
+
known = {p for t in types for p in (ENTITY_PROPERTIES.get(t) or CONTENT_ITEM_PROPERTIES.get(t) or [])}
|
|
206
|
+
if types and name not in known:
|
|
207
|
+
raise ValueError(f"{' / '.join(types)} has no property {name}; it has {', '.join(sorted(known))}")
|
|
202
208
|
return _aggrandize(source, {"Type": "WFPropertyVariableAggrandizement", "PropertyName": name})
|
|
203
209
|
|
|
204
210
|
|
|
211
|
+
def _item_class_of(type_name):
|
|
212
|
+
"""An output type name as a content item class: itself when it is one, else the class that owns it (CONTENT_ITEM_TYPES)."""
|
|
213
|
+
return type_name if type_name in CONTENT_ITEM_PROPERTIES else CONTENT_ITEM_TYPES.get(type_name)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _coerced_class(attachment):
|
|
217
|
+
"""The content item class a reference was last coerced to, when it is one the engine lists; else None."""
|
|
218
|
+
ags = attachment["Value"].get("Aggrandizements")
|
|
219
|
+
last = ags[-1] if isinstance(ags, list) and ags else None
|
|
220
|
+
cls = last.get("CoercionItemClass") if isinstance(last, dict) and last.get("Type") == "WFCoercionVariableAggrandizement" else None
|
|
221
|
+
return cls if cls in CONTENT_ITEM_PROPERTIES else None
|
|
222
|
+
|
|
223
|
+
|
|
205
224
|
def _aggrandize(source, aggrandizement):
|
|
206
225
|
"""`source` with one more aggrandizement; an action stands for its output."""
|
|
207
226
|
if not _is_reference(source):
|
|
@@ -489,6 +508,15 @@ class Shortcut:
|
|
|
489
508
|
output = (definition or {}).get("Output", {})
|
|
490
509
|
entry["_outputName"] = (output.get("OutputName") if isinstance(output, dict) else None) or catalog.get("output") or (intent or {}).get("output")
|
|
491
510
|
entry["_outputTypes"] = list(catalog.get("outputTypes") or (output.get("Types") if isinstance(output, dict) else []) or [])
|
|
511
|
+
# The engine's answer to what comes out: for a choice that decides it (OUTPUT_CLASSES_BY), the literal given selects
|
|
512
|
+
# its classes and a reference means any of them; otherwise the classes for the default parameters.
|
|
513
|
+
by = OUTPUT_CLASSES_BY.get(identifier) if isinstance(identifier, str) else None
|
|
514
|
+
deciding = next((k for k in by if k in params), None) if by else None
|
|
515
|
+
if deciding:
|
|
516
|
+
v = params[deciding]
|
|
517
|
+
entry["_outputClasses"] = list(by[deciding][v]) if isinstance(v, str) and v in by[deciding] else sorted({c for cs in by[deciding].values() for c in cs})
|
|
518
|
+
elif catalog.get("outputClasses"):
|
|
519
|
+
entry["_outputClasses"] = list(catalog["outputClasses"])
|
|
492
520
|
if isinstance(entry["_outputName"], dict):
|
|
493
521
|
entry["_outputName"] = entry["_outputName"].get("format")
|
|
494
522
|
self.actions.append(entry)
|
|
@@ -604,7 +632,11 @@ def get_action(identifier, definitions=DEFINITIONS):
|
|
|
604
632
|
if entry is None:
|
|
605
633
|
return None
|
|
606
634
|
out = {"identifier": identifier, "name": entry["name"], "params": list(entry["params"]),
|
|
607
|
-
"kinds": dict(PARAM_KINDS.get(identifier, {})), "output": entry.get("output"), "outputTypes": list(entry.get("outputTypes", []))
|
|
635
|
+
"kinds": dict(PARAM_KINDS.get(identifier, {})), "output": entry.get("output"), "outputTypes": list(entry.get("outputTypes", [])),
|
|
636
|
+
"inputTypes": list(entry.get("inputTypes", [])), "keywords": list(entry.get("keywords", []))}
|
|
637
|
+
for k in ("outputClasses", "inputKey", "inputRequired", "description", "descriptionNote", "descriptionResult", "discontinued", "unavailable", "requiresUnlock", "disabledOn", "requires"):
|
|
638
|
+
if entry.get(k):
|
|
639
|
+
out[k] = copy.deepcopy(entry[k])
|
|
608
640
|
if entry.get("descriptor"):
|
|
609
641
|
out["descriptor"] = entry["descriptor"]
|
|
610
642
|
defs = _load_definitions(definitions) if definitions else {}
|
|
@@ -613,6 +645,20 @@ def get_action(identifier, definitions=DEFINITIONS):
|
|
|
613
645
|
return out
|
|
614
646
|
|
|
615
647
|
|
|
648
|
+
def accepted_by(type_name):
|
|
649
|
+
"""Where a value of one content type can go: every built-in whose input takes it (with the key the input
|
|
650
|
+
lands in) and every App Intents parameter typed as that entity, as (identifier, key) pairs. Declared types
|
|
651
|
+
only; Shortcuts converts between many types at run time, so this is what the editor would list."""
|
|
652
|
+
out = [(i, e.get("inputKey") or "WFInput") for i, e in ACTIONS.items() if type_name in e.get("inputTypes", [])]
|
|
653
|
+
out += [(i, k) for i, keys in PARAM_ENTITY_TYPES.items() for k, t in keys.items() if t == type_name]
|
|
654
|
+
return out
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
def produced_by(type_name):
|
|
658
|
+
"""Every action whose output is declared as this content type (ACTIONS[id]["outputTypes"])."""
|
|
659
|
+
return [i for i, e in ACTIONS.items() if type_name in e.get("outputTypes", []) or type_name in e.get("outputClasses", [])]
|
|
660
|
+
|
|
661
|
+
|
|
616
662
|
def demo(out):
|
|
617
663
|
out = pathlib.Path(out)
|
|
618
664
|
s = Shortcut(out.stem, color="Teal")
|
|
@@ -631,13 +677,13 @@ def demo(out):
|
|
|
631
677
|
|
|
632
678
|
|
|
633
679
|
from . import actions # noqa: E402 (generated identifier constants and ACTIONS metadata)
|
|
634
|
-
from .actions import ACTIONS, PARAM_KINDS, PARAM_CHOICES, PARAM_VARIABLE_TYPES # noqa: E402
|
|
635
|
-
from .entities import FILTERS, FILTER_OPERATORS, CALENDAR_UNITS, BYTE_COUNT_UNITS, BOUNDED_FILTERS, ENTITY_PROPERTIES, CONTENT_ITEM_CLASSES # noqa: E402 (generated Find filters, entity properties
|
|
680
|
+
from .actions import ACTIONS, PARAM_KINDS, PARAM_CHOICES, PARAM_VARIABLE_TYPES, PARAM_ENTITY_TYPES, OUTPUT_CLASSES_BY # noqa: E402
|
|
681
|
+
from .entities import FILTERS, FILTER_OPERATORS, CALENDAR_UNITS, BYTE_COUNT_UNITS, BOUNDED_FILTERS, ENTITY_PROPERTIES, CONTENT_ITEM_CLASSES, CONTENT_ITEM_PROPERTIES, CONTENT_ITEM_TYPES # noqa: E402 (generated Find filters, entity properties, content item classes, their properties and owned types)
|
|
636
682
|
|
|
637
683
|
PROVENANCE = json.load(open(HERE / "data" / "provenance.json")) if (HERE / "data" / "provenance.json").exists() else {}
|
|
638
684
|
"""Which macOS and Shortcuts build the bundled data was extracted from."""
|
|
639
685
|
|
|
640
|
-
__all__ = ["actions", "ACTIONS", "PARAM_KINDS", "PARAM_CHOICES", "PARAM_VARIABLE_TYPES", "PROVENANCE", "get_action", "repeat_item", "repeat_index", "subject", "Shortcut", "ref", "variable", "shortcut_input", "clipboard", "current_date", "ask", "device_details", "current_app", "picker", "text",
|
|
641
|
-
"property_", "coerce", "dictionary", "app_intent", "where", "where_any", "FILTERS", "FILTER_OPERATORS", "CALENDAR_UNITS", "BYTE_COUNT_UNITS", "BOUNDED_FILTERS", "ENTITY_PROPERTIES", "CONTENT_ITEM_CLASSES",
|
|
686
|
+
__all__ = ["actions", "ACTIONS", "PARAM_KINDS", "PARAM_CHOICES", "PARAM_VARIABLE_TYPES", "PROVENANCE", "get_action", "accepted_by", "produced_by", "PARAM_ENTITY_TYPES", "OUTPUT_CLASSES_BY", "repeat_item", "repeat_index", "subject", "Shortcut", "ref", "variable", "shortcut_input", "clipboard", "current_date", "ask", "device_details", "current_app", "picker", "text",
|
|
687
|
+
"property_", "coerce", "dictionary", "app_intent", "where", "where_any", "FILTERS", "FILTER_OPERATORS", "CALENDAR_UNITS", "BYTE_COUNT_UNITS", "BOUNDED_FILTERS", "ENTITY_PROPERTIES", "CONTENT_ITEM_CLASSES", "CONTENT_ITEM_PROPERTIES", "CONTENT_ITEM_TYPES",
|
|
642
688
|
"ICON_COLORS", "CONDITION", "DEFAULT_GLYPH", "LEGACY_KEYS", "demo"]
|
|
643
|
-
__version__ = "3.
|
|
689
|
+
__version__ = "3.1.0"
|