Bring across all my TextExpander snippets
- ID
192e10c- date
2024-04-09 10:48:17+00:00- author
Alex Chan <alex@alexwlchan.net>- parent
50eac99- message
Bring across all my TextExpander snippets- changed files
11 files, 310 additions, 20 deletionscreate_snippets.pyexpansions/before_and_after.htmlexpansions/create_hash.pyexpansions/datetime_encoder.pyexpansions/get_boto3_session.pyexpansions/get_file_paths.pyexpansions/get_secrets_manager_secret.pyexpansions/list_dynamodb_rows.pyexpansions/list_s3_objects.pyexpansions/mit_license.txtexpansions/template.svg
Changed files
create_snippets.py (2538) → create_snippets.py (6779)
diff --git a/create_snippets.py b/create_snippets.py
index 3ee659f..034def5 100755
--- a/create_snippets.py
+++ b/create_snippets.py
@@ -9,6 +9,11 @@ import uuid
import zipfile
+def read(name):
+ with open(os.path.join('expansions', name)) as infile:
+ return infile.read()
+
+
# fmt: off
SNIPPETS = {
# =========================
@@ -27,7 +32,145 @@ SNIPPETS = {
# =========================
# English words and phrases
# =========================
+ "a11e": "accessible",
+ "a11y": "accessibility",
+ "acc't": "account",
+ "acct": "account",
+ "afaict": "as far as I can tell",
+ "atm": "at the moment",
+ "avg": "average",
+ "bdy": "boundary",
+ "cafe": "café",
+ "cliche": "cliché",
+ "ctd": "continued",
+ "cts": "continuous",
+ "Das Ubermensch": "Das Übermensch",
+ "defn": "definition",
+ "deja vu": "déjà vu",
+ "dept.": "department",
+ "distn": "distribution",
+ "eqn": "equation",
+ "expt": "experiment",
+ "fdn": "foundation",
+ "fiance": "fiancé",
+ "fn": "function",
+ "Gdn": "Garden",
+ "gov't": "government",
+ "govt": "government",
+ "i14n": "intersectional",
+ "i18n": "internationalisation",
+ "iff": "if and only if",
"ina11e": "inaccessible",
+ "indpt": "independent",
+ "intl.": "international",
+ "iptic": "in particular",
+ "l12n": "localisation",
+ "lmk": "let me know",
+ "mgmt": "management",
+ "mgr": "manager",
+ "naive": "naïve",
+ "natl.": "national",
+ "nbhd": "neighbourhood",
+ "nee ": "née ",
+ "o/w": "otherwise",
+ "ofc": "of course",
+ "ptic": "particular",
+ "rec'd": "recommended",
+ "reln": "relation",
+ "reqd": "required",
+ "s.t.": "such that",
+ "soln": "solution",
+ "spose": "suppose",
+ "stdlib": "standard library",
+ "thm": "theorem",
+ "w/b": "week beginning",
+ "w/e": "week ending",
+ "w/o": "without",
+ "y'day": "yesterday",
+
+ # =============================
+ # Fix my common typing mistakes
+ # =============================
+ "cunt": "count",
+ "EVentbridge": "EventBridge",
+ " ot ": " to ",
+ "thier": "their",
+ "WHy": "Why",
+
+ # ============
+ # Proper nouns
+ # ============
+ "Agnes": "Agnès",
+ "B'ham": "Birmingham",
+ "BackBlaze": "Backblaze",
+ "Bezier": "Bézier",
+ "CF": "CloudFront",
+ "CO2": "CO₂",
+ "China Mieville": "China Miéville",
+ "Cloudwatch": "CloudWatch",
+ "Ebay": "eBay",
+ "El Otro Periodico": "El Otro Periódico",
+ "Elasticache": "ElastiCache",
+ "Eventbridge": "EventBridge",
+ "Facetime": "FaceTime",
+ "FastMail": "Fastmail",
+ "Gitbook": "GitBook",
+ "Hashicorp": "HashiCorp",
+ "Maciej Ceglowski": "Maciej Cegłowski",
+ "Paypal": "PayPal",
+ "Phylopic": "PhyloPic",
+ "Postgresql": "PostgreSQL",
+ "Powerpoint": "PowerPoint",
+ "Raphaelle": "Raphaëlle",
+ "Regents Canal": "Regent’s Canal",
+ "Rubocop": "RuboCop",
+ "Sqlite": "SQLite",
+ "SQlite": "SQLite",
+ "Sean": "Seán",
+ "Sharepoint": "SharePoint",
+ "Skoda": "Škoda",
+ "Smugmug": "SmugMug",
+ "Taf": "Tâf",
+ "Textexpander": "TextExpander",
+ "Whatsapp": "WhatsApp",
+ "WikiData": "Wikidata",
+ "Wordpress": "WordPress",
+ "Youtube": "YouTube",
+ "Zoe": "Zoë",
+ "bhalaj": "bhålaj",
+ "ldn": "london",
+ "wall-e": "WALL·E",
+
+ # =================================
+ # Symbols and other bits of Unicode
+ # =================================
+ "180^": "180°",
+ "^C": "°C",
+ "^F": "°F",
+ "^deg": "°",
+ "^ft": "′",
+ "^in": "″",
+ ":+1:": "👍",
+ ":wave:": "👋",
+ ";1/2": "½",
+ ";3/4": "¾",
+ ";alt": "⌥",
+ ";approx": "≈",
+ ";bullet": "•",
+ ";cmd": "⌘",
+ ";ctrl": "⌃",
+ ";dot": "·",
+ ";eur": "€",
+ ";minus": "−",
+ ";opt": "⌥",
+ ";pi": "π",
+ ";pm": "±",
+ ";sec": "§",
+ ";shift": "⇧",
+ ";sqrt": "√",
+ ";times": "×",
+ ";tm": "™",
+ ";zwsp": "\u200b", # zero-width space
# =============
# Personal info
@@ -37,30 +180,47 @@ SNIPPETS = {
";ale": "alexwlchan",
- # =================================
- # Programming: shebangs for scripts
- # =================================
+ # ====================
+ # Programming snippets
+ # ====================
"!bash": "#!/usr/bin/env bash\n\nset -o errexit\nset -o nounset\n",
"!osa": "#!/usr/bin/env osascript\n",
"!py": "#!/usr/bin/env python3\n\n",
"!rb": "#!/usr/bin/env ruby\n",
"!swift": "#!/usr/bin/env swift\n",
- # ============================
- # Programming: Python snippets
- # ============================
+ "!rect": '<rect width="500" height="250" fill="yellow"/>',
+ "!svg": read("template.svg"),
+
+ "!before": read("before_and_after.html"),
+
+ "!mit": read("mit_license.txt"),
+
+ # Git trailer
+ # See https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors
+ ";co": "Co-authored-by:",
+
+ # ===================================
+ # Python-related programming snippets
+ # ===================================
"!j": "import json\n",
-}
-# fmt: on
+ "!pp": "from pprint import pprint; pprint({cursor})",
+ "@param": "@pytest.mark.parametrize({cursor})",
+ "!flapi": read("flapi.py"),
-# fmt: off
-LONGER_SNIPPETS = {
- # ==============================
- # Programming: fragments of code
- # ==============================
- "!flapi": "flapi.py",
+ "py!aws": read("get_boto3_session.py"),
+ "py!dy": read("list_dynamodb_rows.py"),
+ "py!h": read("create_hash.py"),
+ "py!jd": read("datetime_encoder.py"),
+ "py!pth": read("get_file_paths.py"),
+ "py!sec": read("get_secrets_manager_secret.py"),
+ "py!s3": read("list_s3_objects.py"),
+
+ # I can never remember the order of args to this function,
+ # so when I start typing it, add a comment to help me out.
+ "datetime.datetime.strp": "datetime.datetime.strptime({cursor}) # date_string, format",
}
# fmt: on
@@ -97,10 +257,4 @@ if __name__ == "__main__":
for shortcut, expansion in SNIPPETS.items():
add_snippet(zf, shortcut, expansion)
- for shortcut, filename in LONGER_SNIPPETS.items():
- with open(pathlib.Path("expansions") / filename) as f:
- expansion = f.read()
-
- add_snippet(zf, shortcut, expansion)
-
subprocess.check_call(["open", "Alex’s snippets.alfredsnippets"])
expansions/before_and_after.html (0) → expansions/before_and_after.html (101)
diff --git a/expansions/before_and_after.html b/expansions/before_and_after.html
new file mode 100644
index 0000000..bfc6fb8
--- /dev/null
+++ b/expansions/before_and_after.html
@@ -0,0 +1,10 @@
+<table>
+<tr>
+<th>Before</th>
+<th>After</th>
+</tr>
+<tr>
+<td>BEFORE</td>
+<td>AFTER</td>
+</tr>
+</table>
expansions/create_hash.py (0) → expansions/create_hash.py (287)
diff --git a/expansions/create_hash.py b/expansions/create_hash.py
new file mode 100644
index 0000000..5950741
--- /dev/null
+++ b/expansions/create_hash.py
@@ -0,0 +1,14 @@
+import hashlib
+
+
+def create_hash(path, *, algorithm=hashlib.sha256):
+ """
+ Returns the hex checksum of the given path.
+ """
+ h = algorithm()
+
+ with open(path, "rb") as infile:
+ while chunk := infile.read(8192):
+ h.update(chunk)
+
+ return h.hexdigest()
expansions/datetime_encoder.py (0) → expansions/datetime_encoder.py (169)
diff --git a/expansions/datetime_encoder.py b/expansions/datetime_encoder.py
new file mode 100644
index 0000000..8b6c1a5
--- /dev/null
+++ b/expansions/datetime_encoder.py
@@ -0,0 +1,7 @@
+import datetime
+
+
+class DatetimeEncoder(json.JSONEncoder):
+ def default(self, obj):
+ if isinstance(obj, datetime.datetime):
+ return obj.isoformat()
expansions/get_boto3_session.py (0) → expansions/get_boto3_session.py (484)
diff --git a/expansions/get_boto3_session.py b/expansions/get_boto3_session.py
new file mode 100644
index 0000000..ee8a15b
--- /dev/null
+++ b/expansions/get_boto3_session.py
@@ -0,0 +1,15 @@
+import boto3
+
+
+def get_aws_session(*, role_arn: str) -> boto3.Session:
+ sts_client = boto3.client("sts")
+ assumed_role_object = sts_client.assume_role(
+ RoleArn=role_arn, RoleSessionName="AssumeRoleSession1"
+ )
+ credentials = assumed_role_object["Credentials"]
+
+ return boto3.Session(
+ aws_access_key_id=credentials["AccessKeyId"],
+ aws_secret_access_key=credentials["SecretAccessKey"],
+ aws_session_token=credentials["SessionToken"],
+ )
expansions/get_file_paths.py (0) → expansions/get_file_paths.py (598)
diff --git a/expansions/get_file_paths.py b/expansions/get_file_paths.py
new file mode 100644
index 0000000..bd9e1c9
--- /dev/null
+++ b/expansions/get_file_paths.py
@@ -0,0 +1,25 @@
+import pathlib
+
+
+def get_file_paths_under(root=".", *, suffix=""):
+ """
+ Generates the absolute paths to every matching file under ``root``.
+ """
+ root = pathlib.Path(root)
+
+ if root.exists() and not root.is_dir():
+ raise ValueError(f"Cannot find files under file: {root!r}")
+
+ if not root.is_dir():
+ raise FileNotFoundError(root)
+
+ for dirpath, _, filenames in root.walk():
+ for f in filenames:
+ p = dirpath / f
+
+ if p.is_file() and f.lower().endswith(suffix):
+ yield p
+
+
+for p in get_file_paths_under():
+ {cursor}
expansions/get_secrets_manager_secret.py (0) → expansions/get_secrets_manager_secret.py (273)
diff --git a/expansions/get_secrets_manager_secret.py b/expansions/get_secrets_manager_secret.py
new file mode 100644
index 0000000..26fa516
--- /dev/null
+++ b/expansions/get_secrets_manager_secret.py
@@ -0,0 +1,9 @@
+def get_secret_string(sess: boto3.Session, **kwargs) -> str:
+ """
+ Look up a SecretString from Secrets Manager, and return the string.
+ """
+ secrets = sess.client("secretsmanager")
+
+ resp = secrets.get_secret_value(**kwargs)
+
+ return resp["SecretString"]
expansions/list_dynamodb_rows.py (0) → expansions/list_dynamodb_rows.py (661)
diff --git a/expansions/list_dynamodb_rows.py b/expansions/list_dynamodb_rows.py
new file mode 100644
index 0000000..cca105b
--- /dev/null
+++ b/expansions/list_dynamodb_rows.py
@@ -0,0 +1,19 @@
+import boto3
+
+
+def scan_table(sess: boto3.Session, *, TableName: str, **kwargs):
+ """
+ Generates all the items in a DynamoDB table.
+
+ :param dynamo_client: A boto3 client for DynamoDB.
+ :param TableName: The name of the table to scan.
+
+ Other keyword arguments will be passed directly to the Scan operation.
+ See https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#DynamoDB.Client.scan
+
+ """
+ dynamo_client = sess.resource("dynamodb").meta.client
+ paginator = dynamo_client.get_paginator("scan")
+
+ for page in paginator.paginate(TableName=TableName, **kwargs):
+ yield from page["Items"]
expansions/list_s3_objects.py (0) → expansions/list_s3_objects.py (196)
diff --git a/expansions/list_s3_objects.py b/expansions/list_s3_objects.py
new file mode 100644
index 0000000..d23b1b0
--- /dev/null
+++ b/expansions/list_s3_objects.py
@@ -0,0 +1,5 @@
+def list_s3_objects(sess: boto3.Session, **kwargs):
+ s3 = sess.client("s3")
+
+ for page in s3.get_paginator("list_objects_v2").paginate(**kwargs):
+ yield from page.get("Contents", [])
expansions/mit_license.txt (0) → expansions/mit_license.txt (1063)
diff --git a/expansions/mit_license.txt b/expansions/mit_license.txt
new file mode 100644
index 0000000..2cd802b
--- /dev/null
+++ b/expansions/mit_license.txt
@@ -0,0 +1,19 @@
+Copyright (c) {isodate:yyyy} Alex Chan
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
expansions/template.svg (0) → expansions/template.svg (273)
diff --git a/expansions/template.svg b/expansions/template.svg
new file mode 100644
index 0000000..fdd81c1
--- /dev/null
+++ b/expansions/template.svg
@@ -0,0 +1,13 @@
+<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
+ <defs>
+ <style>
+ line {
+ stroke: currentColor;
+ stroke-width: 1.5;
+ stroke-linecap: round;
+ }
+ </style>
+ </defs>
+
+ <rect width="200" height="200" fill="yellow"/>
+</svg>