Monday, 8 September 2025

How to Make Your iPhone Battery Last Long

Stop “battery anxiety” with easy settings, smart charging habits, and a few myth-busting facts. Bookmark this guide and revisit the monthly checklist at the end.

Quick Wins (5 minutes)

1) Enable Low Power Mode

Swipe down ➝ Control Center ➝ tap the battery icon. Or go: Settings → Battery → Low Power Mode. Cuts background activity and visual effects.

2) Dim & Auto-Lock

Settings → Display & Brightness: lower brightness, set Auto-Lock to 30 seconds–1 minute, disable Always-On (on supported devices) to save hours daily.

3) Tame Location & Background Refresh

Settings → Privacy & Security → Location Services: switch rarely used apps to While Using. Then Settings → General → Background App Refresh: disable for non-essential apps.

4) Silence Non-Critical Pings

Settings → Notifications: turn off unneeded notifications & Live Activities. Every wake costs energy.

Settings to Tweak (and why they work)

SettingPathWhat to ChooseWhy It Helps
Battery Insights Settings → Battery Check Battery Usage by App Find energy-hungry apps; adjust their permissions or usage.
Mail Fetch Settings → Mail → Accounts → Fetch New Data Use Push for main account, Manual/Hourly for others Reduces constant network wake-ups.
Location Settings → Privacy & Security → Location Services Prefer While Using, disable Precise where not needed GPS & radios are battery-intensive; precision costs more.
Background App Refresh Settings → General → Background App Refresh Off for games/social/shopping you don’t need updated Stops silent network/CPU work when you’re not using the app.
Display Settings → Display & Brightness Lower brightness; disable Always-On (if available); shorter Auto-Lock Screen is the #1 daily drain; less on-time = more battery.
Haptics & System Sounds Settings → Sounds & Haptics Reduce haptics for keyboard/system Vibration motor uses more power than a simple “click”.
Widgets & Live Activities Home/Lock Screen customization Keep only essential, remove live-updating tiles Frequent refreshes wake CPU and network.
Siri & Search Settings → Siri & Search Disable “Hey Siri” if you rarely use it Always-listening voice detection costs idle power.
App Permissions Settings → [App] Block background cellular data for non-essentials Prevents stealthy syncs when on mobile data.
Pro Tip

Keep iOS and apps up-to-date. Updates often fix runaway background tasks and improve power management.

Travel & Low-Signal Tips

  • Weak signal = hungry radios. If coverage is poor and you just need offline access, turn on Airplane Mode and enable Wi-Fi as needed.
  • Use Low Data Mode in Settings → Cellular → Cellular Data Options to reduce background syncing on trips.
  • Download maps/music for offline use before you go.

Diagnose Fast Drain (Step-by-Step)

  1. Open Battery (Settings → Battery) and view the last 24 hours. Identify top draining apps.
  2. Adjust offenders:
    • Set Location to While Using, disable Precise if not needed.
    • Disable Background App Refresh for that app.
    • Reduce notifications or turn off Live Activities.
  3. Check storage: Settings → General → iPhone Storage. Low free space can cause extra background work.
  4. Restart the device; stuck processes clear and radios reset.
  5. Reset settings (last resort): Settings → General → Transfer or Reset iPhone → Reset → Reset All Settings. This doesn’t delete data but resets preferences.

Myths vs Facts

Myth: “You must always go 0% → 100%.”

Fact: Frequent partial charges are healthier for lithium-ion than deep cycles.

Myth: “Force-quitting apps saves battery.”

Fact: iOS suspends apps efficiently; relaunching repeatedly can use more power.

Myth: “Only dark mode matters.”

Fact: Brightness and screen on-time dominate. Dark mode helps, but less than simply dimming.

Myth: “Wireless charging is always bad.”

Fact: Heat is the issue. Cool, quality wireless chargers are fine; avoid hot surfaces.

Monthly Maintenance Checklist

  • Review Battery → Usage by App; tame any new power hogs.
  • Purge or offload apps you don’t use.
  • Update iOS and apps.
  • Scan notifications and disable noisy ones.
  • Clean charging ports and cables; replace frayed cables.
  • Skim Privacy → Location Services for apps that don’t need Always-on.

Tip: Add this checklist as a monthly reminder.

Sunday, 7 September 2025

The Littke Known Settings on Your iPhone (That Actually Make Life Easier)

Unlock power features you already own: faster actions, calmer notifications, stronger privacy, and “why didn’t I know this?” automations.

1) Back Tap = Secret Double-Tap Shortcuts

Settings → Accessibility → Touch → Back Tap

Assign actions to double- or triple-tap the back of your iPhone. Great for screenshotting, launching the camera, toggling the flashlight, or running a Shortcut.

Ideas

  • Double-tap → Screenshot
  • Triple-tap → Open Camera or Toggle Flashlight
  • Run a Shortcut: log water intake, append a note, start a timer
Pro tip: Pair Back Tap with a Shortcut that toggles Low Power Mode for instant battery savings.

2) Focus That Changes with Time/Place

Settings → Focus → Work/Driving/Sleep/Custom → Add Schedule

Focus lets you filter notifications and Home Screens. The gold is in automation: trigger a Focus by time, location, or app use.

  • Create a “Deep Work” Focus.
  • Allow only critical people/apps.
  • Add a schedule for weekdays 9–12, or “when I arrive at the office”.
  • Set a custom Home Screen that hides distracting apps during that Focus.
Bonus: Link a Focus to a Lock Screen style (e.g., minimal widgets) for a distraction-free vibe.

3) Sound Recognition & Background Sounds

Settings → Accessibility → Sound Recognition

Your iPhone can listen for specific sounds (doorbell, smoke alarm, baby crying) and notify you.

Settings → Accessibility → Audio/Visual → Background Sounds

Play gentle ambient audio (rain, ocean, stream) under music or alone to improve focus or sleep.

Note: Sound Recognition may reduce Siri “Hey Siri” performance and can affect battery life.

4) Make Haptic Touch Faster

Settings → Accessibility → Touch → Haptic Touch

Set the “Touch Duration” to Fast for snappier menus and link previews.

5) Keyboard Power Moves & Text Replacements

Settings → General → Keyboard → Text Replacement
  • Create snippets like ;em → your email, ;addr → your mailing address.
  • Enable One-Handed Keyboard (press and hold globe/emoji icon).
  • Trackpad mode: press and hold the space bar, then move the cursor with your thumb.

6) Privacy That Actually Matters: Location & Wi-Fi

Per-App “Precise Location”

Settings → Privacy & Security → Location Services → App → Precise Location

Turn off Precise for apps that don’t need your exact spot (e.g., weather can use approximate).

System Services (the “hidden” list)

Settings → Privacy & Security → Location Services → System Services
  • Significant Locations: clear or disable if you prefer not to save a travel log.
  • Product Improvement toggles: opt out if you want fewer background pings.

Private Wi-Fi Address

Settings → Wi-Fi → (ⓘ for your network) → Private Wi-Fi Address

Helps reduce tracking across networks. If a network blocks you, toggle off temporarily, then back on.

iCloud+ Privacy

Settings → [Your Name] → iCloud → iCloud Private Relay / Hide My Email

Private Relay obscures IP/addressing in Safari. Hide My Email creates burner addresses that forward to your inbox.

7) Safety Check & Emergency SOS

Settings → Privacy & Security → Safety Check

Quickly audit and revoke access you’ve shared (location, photos, notes) with people or apps.

Settings → Emergency SOS
  • Enable “Call with Hold and Release” or “Call with 5 Button Presses”.
  • Add emergency contacts in Health → Medical ID.
Heads-up: Test features carefully—triggering SOS will call local emergency services.

8) Lockdown Mode (for High-Risk Users)

Settings → Privacy & Security → Lockdown Mode

Extremely limits attack surface by restricting attachments, web tech, and invitations. Turn on only if you believe you’re at specific risk.

9) Battery Health, Optimized Charging & Clean Energy

Settings → Battery → Battery Health & Charging
  • Optimized Battery Charging learns your routine to reduce aging.
  • Clean Energy Charging (region-dependent) favors times when the grid is cleaner.
  • Check Battery Usage by App to spot silent drainers.
Quick win: Add a Low Power Mode toggle to Control Center: Settings → Control Center → + Low Power Mode.

10) Auto-Answer, Silence Unknown Callers & Announce Notifications

Auto-Answer Calls

Settings → Accessibility → Touch → Call Audio Routing → Auto-Answer Calls

Great for gloves, workouts, or if you often miss calls on the move.

Silence Unknown Callers

Settings → Phone → Silence Unknown Callers

Unknown numbers go to voicemail; you still see them in Recents.

Announce Notifications

Settings → Siri & Search → Announce Notifications

Hear important messages via AirPods/CarPlay without looking at your screen.

11) Shortcuts: NFC, Menus & Personal Automations

Shortcuts app → Automations → New Automation
  • NFC tag: tap your phone to a sticker to start a routine (log gym set, open a playlist, run a Home scene).
  • Time of day: auto-enable a Focus, send a message, or speak a reminder.
  • App opens: when Camera opens, set brightness to 100% and enable Do Not Disturb for 10 minutes.
Pro tip: Create a “Share Sheet” shortcut to instantly resize images or create a PDF from photos.

12) AirDrop, NameDrop & “Share via Camera”

Settings → General → AirDrop
  • NameDrop: Quickly share contact posters by bringing iPhones together (toggle in AirDrop settings).
  • Receiving: Set to Contacts Only by default; switch to Everyone for 10 Minutes when needed.
  • Share via Camera: Scan a QR code to add contacts or join Wi-Fi quickly.

Quick Reference Table

Setting Path What It Does Best For
Back Tap Accessibility → Touch → Back Tap Double/triple-tap back to trigger actions Fast screenshots, flashlight, Shortcuts
Focus Schedules Focus → Add Schedule Time/location/app-based notification filters Work blocks, driving, sleep hygiene
Sound Recognition Accessibility → Sound Recognition Alerts for alarms, doorbells, baby crying Hearing support, home awareness
Background Sounds Accessibility → Audio/Visual Play ambient audio under other apps Focus, masking noise, sleep
Haptic Touch Speed Accessibility → Touch → Haptic Touch Quicker context menus Power users
Text Replacement General → Keyboard Expand snippets into full text Emails, addresses, canned replies
Precise Location Privacy & Security → Location Services Limit exact GPS per app Privacy without breaking features
System Services Privacy & Security → Location → System Services Control Apple/location diagnostics Battery, privacy tuning
Private Wi-Fi Address Wi-Fi → (ⓘ) Randomizes MAC to reduce tracking Public networks
Safety Check Privacy & Security Review/revoke shared access quickly Safety & privacy hygiene
Lockdown Mode Privacy & Security Extreme hardening for high-risk users Journalists, activists, targets
Battery Health & Charging Battery Optimize charge & reduce aging Longevity, greener charging
Auto-Answer / Silence Unknown Accessibility → Touch / Phone Hands-free pickup; cut spam calls Workouts, focus time, travel
Shortcuts Automations Shortcuts → Automations Run actions by NFC/time/app Home, health, journaling
AirDrop & NameDrop General → AirDrop Faster sharing and contact exchange Events, meetings, classrooms

FAQ & Notes

  • Will these paths match my iOS exactly? Menu names can vary slightly between iOS 15–18 and by region. If you can’t find an item, use the search bar at the top of Settings.
  • Will this drain battery? Features like Sound Recognition and Background Sounds use more power. Toggle when needed.
  • Can I revert? Yes—just switch off the toggle or remove an automation.
Save this post: Bookmark and revisit after updates—Apple often adds options under the same menu names.

Python 3 Unit Testing

Python 3 · Testing

Learn how to write fast, reliable tests using the built-in unittest and the popular pytest framework—plus fixtures, mocking, parameterization, coverage, and CI.

1) Why Unit Testing?

Unit tests check small, isolated pieces of code (functions/classes) to catch bugs early. They also document behavior and enable safe refactoring. With a fast test suite, you’ll ship changes with confidence.

2) A Clean Project Structure

Recommended layout

myapp/
├─ src/
│  └─ myapp/
│     ├─ __init__.py
│     ├─ mathy.py
│     └─ io_helpers.py
├─ tests/
│  ├─ test_mathy.py
│  └─ test_io_helpers.py
├─ pyproject.toml
└─ README.md

Tip: Put application code in src/myapp and tests in tests/. Tools like pytest discover tests named test_*.py.

Example code (to be tested)

# src/myapp/mathy.py
def add(a: float, b: float) -> float:
    return a + b

def divide(a: float, b: float) -> float:
    if b == 0:
        raise ZeroDivisionError("b must not be zero")
    return a / b

3) The Built-in unittest (Batteries Included)

unittest ships with Python 3—no install needed.

# tests/test_mathy_unittest.py
import unittest
from myapp.mathy import add, divide

class TestMathy(unittest.TestCase):
    def test_add(self):
        self.assertEqual(add(2, 3), 5)
        self.assertAlmostEqual(add(0.1, 0.2), 0.3, places=7)

    def test_divide_ok(self):
        self.assertEqual(divide(10, 2), 5.0)

    def test_divide_by_zero(self):
        with self.assertRaises(ZeroDivisionError) as cm:
            divide(1, 0)
        self.assertIn("must not be zero", str(cm.exception))

if __name__ == "__main__":
    unittest.main()

Run: python -m unittest or python -m unittest tests/test_mathy_unittest.py

4) pytest (Fast, Friendly, Powerful)

Install with pip install pytest then run pytest. It offers concise tests, rich output, and powerful plugins.

# tests/test_mathy_pytest.py
from myapp.mathy import add, divide
import pytest

def test_add():
    assert add(2, 3) == 5

def test_divide_ok():
    assert divide(9, 3) == 3.0

def test_divide_by_zero():
    with pytest.raises(ZeroDivisionError):
        divide(1, 0)

Run: pytest -q

5) Fixtures: Reusable Setup/Teardown

Fixtures provide test data or resources and clean up automatically.

# tests/conftest.py
import tempfile, shutil, pathlib, pytest

@pytest.fixture
def temp_dir():
    d = tempfile.mkdtemp()
    try:
        yield pathlib.Path(d)
    finally:
        shutil.rmtree(d)

# tests/test_io_helpers.py
def test_write_and_read(temp_dir):
    p = temp_dir / "hello.txt"
    p.write_text("hi")
    assert p.read_text() == "hi"

Tip: Place shared fixtures in tests/conftest.py for auto-discovery.

6) Mocking with unittest.mock

Mock external calls (network, file system, time) so tests are fast and deterministic.

# src/myapp/io_helpers.py
import requests

def fetch_title(url: str) -> str:
    r = requests.get(url, timeout=5)
    r.raise_for_status()
    return r.text.split("<title>")[1].split("</title>")[0]

# tests/test_io_helpers_mock.py
from unittest.mock import patch, MagicMock
from myapp.io_helpers import fetch_title

@patch("myapp.io_helpers.requests.get")
def test_fetch_title_mocks_requests(mock_get):
    fake = MagicMock()
    fake.text = "<html><title>Hello</title></html>"
    fake.raise_for_status = lambda: None
    mock_get.return_value = fake

    assert fetch_title("https://example.com") == "Hello"
    mock_get.assert_called_once()

Rule of thumb: Patch where the object is used, not where it’s defined.

7) Parameterization (More Cases, Less Boilerplate)

# tests/test_mathy_param.py
import pytest
from myapp.mathy import add

@pytest.mark.parametrize(
    "a,b,expected",
    [
        (0, 0, 0),
        (2, 3, 5),
        (-1, 5, 4),
        (0.1, 0.2, 0.3),
    ],
)
def test_add_param(a, b, expected):
    assert add(a, b) == pytest.approx(expected)

Why it rocks: Easier to read, great failure messages, and one test turns into many.

8) Coverage: Know What You Tested

Install pip install coverage, then:

# with pytest
coverage run -m pytest
coverage report -m
coverage html  # open htmlcov/index.html

Aim for meaningful coverage (e.g., 80%+), without gaming the metric.

9) A Tiny TDD Loop

  1. Write a small failing test (red).
  2. Implement the simplest code to pass (green).
  3. Refactor for clarity/perf while keeping tests green.
# 1) failing test first
def test_is_even():  # tests/test_even.py
    from myapp.mathy import is_even
    assert is_even(2) is True
    assert is_even(3) is False

# 2) minimal implementation
# src/myapp/mathy.py
def is_even(n: int) -> bool:
    return (n % 2) == 0

10) Run Tests on Every Push (CI)

Here’s a minimal GitHub Actions workflow to run tests on Python 3.10–3.12 and generate coverage.

# .github/workflows/tests.yml
name: tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.10", "3.11", "3.12"]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: ${{ matrix.python-version }} }
      - run: python -m pip install --upgrade pip
      - run: pip install -e . pytest coverage
      - run: coverage run -m pytest -q
      - run: coverage report -m

Pro tip: Fail the build if coverage drops using coverage report --fail-under=80.

11) Assertions & Cheatsheet

unittest assertions

AssertionUse
assertEqual(a, b)Exact equality
assertAlmostEqual(a, b, places=7)Floats
assertTrue(x) / assertFalse(x)Truthiness
assertIn(x, seq)Membership
assertIsNone(x)None checks
assertRaises(E)Exceptions

pytest power-ups

FeatureExample
Simple assertsassert func(x) == y
Parameterize@pytest.mark.parametrize(...)
Fixtures@pytest.fixture + autouse
Marks@pytest.mark.slow, -m "not slow"
Skip/xfailpytest.skip(), @pytest.mark.xfail

12) FAQ

Do I need both unittest and pytest?

No. Many teams use just pytest for new projects. You can still run legacy unittest tests under pytest.

How many tests should I write?

Focus on critical paths and tricky logic. Aim for high coverage with meaningful scenarios—not just lines executed.

How do I test time, random, or network calls?

Use unittest.mock to patch functions like time.time, random.random, or HTTP clients. Inject dependencies where possible.

Copy-Paste Quickstart

# 1) install
pip install pytest coverage

# 2) create files
mkdir -p src/myapp tests
# add src/myapp/mathy.py and tests/test_mathy_pytest.py (from above)

# 3) run tests + coverage
pytest -q
coverage run -m pytest
coverage report -m

Python 2.7 vs Python 3

What changed, why it matters, and how to migrate safely.

Quick Summary Table

Topic Python 2.7 Python 3.x Notes
Lifecycle End-of-life (EOL) Jan 1, 2020 Actively maintained Security and new features only in Python 3.
Print print "hi" print("hi") (function) Use parentheses in Py3; enables redirection like any function.
Integer Division 5/2 == 2 5/2 == 2.5 Use // for floor division consistently.
Text vs Bytes str = bytes; unicode = text str = text (Unicode); bytes = binary Explicit encodes/decodes in Py3 when crossing boundaries.
Input raw_input() (text), input() evals input() returns text Safer and simpler in Py3.
Range & Iterators xrange() (lazy), range() (list) range() is lazy No xrange in Py3.
Exceptions except ValueError, e: except ValueError as e: New syntax only in Py3.
Libraries Many packages dropped support Full ecosystem support Modern libs target Py3.8+.
Strings Literals "abc" is bytes; u"abc" unicode "abc" is Unicode; b"abc" bytes Prefer Unicode text, bytes for I/O.
F-Strings Not available f"Hello {name}" Fast, readable formatting (Py3.6+).
Type Hints 3rd-party stubs only Built-in typing Better tooling & readability.
Dict Views .keys()/.items() return lists Return dynamic views (iterable) Wrap with list(...) if you need a list.
Unicode I/O Implicit conversions common Explicit, consistent Open files with encoding="utf-8".
Metaclass Syntax __metaclass__ = M class C(metaclass=M): Cleaner and explicit in Py3.
Standard Library Old module names/locations Reorganized (e.g., urllib) Use modern imports or compatibility layers.

Why Python 3 Happened

Python 3 fixed long-standing design issues—especially the confusing split between text and bytes, integer division, and a few syntax inconsistencies. The result is a cleaner language that’s easier to teach, localize, and maintain.

Common Code Differences

Print & Division

# Python 2.7
print "Hello"
print 5/2        # 2
from __future__ import division
print 5/2        # 2.5 with future import
# Python 3.x
print("Hello")
print(5/2)       # 2.5
print(5//2)      # 2  (floor division)

Unicode vs Bytes

# Python 2.7
u_text = u"café"
b_data = "café"              # bytes
print(type(u_text)), print(type(b_data))
# Python 3.x
text = "café"                # str (Unicode)
data = "café".encode("utf-8")# bytes
with open("out.txt", "w", encoding="utf-8") as f:
    f.write(text)

Ranges & Iteration

# Python 2.7
for i in xrange(3):
    pass
# Python 3.x
for i in range(3):  # lazy like xrange
    pass

Exceptions & Input

# Python 2.7
try:
    1/0
except ZeroDivisionError, e:
    print e
name = raw_input("Name: ")
# Python 3.x
try:
    1/0
except ZeroDivisionError as e:
    print(e)
name = input("Name: ")

Standard Library Reorg Highlights

URLs

# Python 2.7
import urllib2
data = urllib2.urlopen("https://example.com").read()
# Python 3.x
from urllib.request import urlopen
data = urlopen("https://example.com").read()

Itertools / Dicts

# Python 2.7
d = {"a":1}
d.keys()    # list
# Python 3.x
d = {"a":1}
list(d.keys())   # materialize if needed

Migration Tips (2.7 → 3)

  • Target a modern Python 3 (3.10+ recommended) for long-term support and features (pattern matching, better typing, speedups).
  • Run python3 -m venv .venv and port code inside an isolated environment.
  • Use 2to3 or python-modernize/futurize for a first pass; then hand-tune text/bytes boundaries.
  • Adopt from __future__ import print_function and unicode_literals in 2.7 to reduce diffs before switching.
  • Replace xrange → range, iteritems() → items(), raw_input → input.
  • Open files explicitly with encodings: open(..., encoding="utf-8").
  • Pin and upgrade dependencies that still assume Py2; most modern versions are Py3-only.
  • Add tests (e.g., pytest), run type checks (mypy), and format (black) once on Py3.
Rule of thumb: Treat all in-memory text as Unicode (str) and only encode/decode at I/O boundaries (files, sockets, HTTP).

Which Should You Use in 2025?

Use Python 3 for everything new. Python 2.7 is unmaintained and unsupported by the modern ecosystem. If you maintain legacy 2.7 code, prioritize a staged migration to Python 3 with automated tests and incremental refactors.

Sunday, 31 August 2025

Creating Your Own Command-Line Tools with Python

Turn tiny scripts into polished CLIs you can install, share, and run from anywhere.

Contents
  1. Why build a CLI?
  2. Prerequisites
  3. A 3-minute CLI with argparse
  4. Recommended project structure
  5. A real app with Typer (Click) + Rich
  6. Package & install with entry points
  7. Testing CLIs
  8. Distribute with pipx & PyPI
  9. Polish: logging, config, shell completion
  10. Common gotchas

1) Why build a CLI?

  • Speed: One command beats 10 clicks.
  • Automation: Scriptable in cron, CI, or other tools.
  • Shareable: Pack once, install anywhere.
  • Composability: Works well with pipes (|) and redirects.

2) Prerequisites

  • Python 3.9+ recommended
  • pip (and optionally pipx)
  • Basic terminal familiarity
Tip: Use a virtual environment (python -m venv .venv then source .venv/bin/activate or .venv\Scripts\activate on Windows) while developing.

3) A 3-minute CLI with argparse

Start simple with the standard library:

# greet/__main__.py
import argparse

def main():
    parser = argparse.ArgumentParser(prog="greet", description="Say hello from your CLI.")
    parser.add_argument("name", help="Who to greet")
    parser.add_argument("--shout", action="store_true", help="Uppercase the greeting")
    args = parser.parse_args()

    msg = f"Hello, {args.name}!"
    if args.shout:
        msg = msg.upper()
    print(msg)

if __name__ == "__main__":
    main()

Run it straight from the module:

python -m greet Remo
python -m greet Remo --shout

Next, we’ll package it so you can just type greet anywhere.

4) Recommended project structure

greet/
├─ greet/
│  ├─ __init__.py
│  └─ __main__.py
├─ pyproject.toml
└─ README.md

5) A real app with Typer (Click) + Rich

Typer (built on Click) gives you subcommands, type hints, smart help, and shell completion with almost no boilerplate. We’ll build a tiny to-do manager called tasker that stores data in ~/.tasker.json and prints pretty tables using Rich.

# tasker_cli/__main__.py
from __future__ import annotations
import json, os
from pathlib import Path
from typing import List, Dict
import typer
from rich.console import Console
from rich.table import Table

app = typer.Typer(add_completion=True)
console = Console()
DATA_FILE = Path(os.getenv("TASKER_FILE", Path.home() / ".tasker.json"))

def load_tasks() -> List[Dict]:
    if DATA_FILE.exists():
        return json.loads(DATA_FILE.read_text())
    return []

def save_tasks(tasks: List[Dict]) -> None:
    DATA_FILE.write_text(json.dumps(tasks, indent=2))

@app.command()
def add(text: str = typer.Argument(..., help="Task description")):
    """Add a new task."""
    tasks = load_tasks()
    next_id = (max([t["id"] for t in tasks]) + 1) if tasks else 1
    tasks.append({"id": next_id, "text": text, "done": False})
    save_tasks(tasks)
    console.print(f"✅ Added task [bold]{text}[/] (id={next_id})")

@app.command("list")
def list_tasks(show_done: bool = typer.Option(True, help="Include completed tasks")):
    """List tasks."""
    tasks = load_tasks()
    table = Table(title="Tasker")
    table.add_column("ID", justify="right")
    table.add_column("Status")
    table.add_column("Task")

    for t in tasks:
        if not show_done and t["done"]:
            continue
        status = "✅" if t["done"] else "•"
        table.add_row(str(t["id"]), status, t["text"])
    console.print(table)

@app.command()
def done(task_id: int = typer.Argument(..., help="Task ID to mark as done")):
    """Mark a task as done."""
    tasks = load_tasks()
    for t in tasks:
        if t["id"] == task_id:
            t["done"] = True
            save_tasks(tasks)
            console.print(f"🎉 Marked task {task_id} as done")
            return
    raise typer.Exit(code=1)

@app.command()
def clear(confirm: bool = typer.Option(False, "--yes", help="Confirm clearing all tasks")):
    """Clear all tasks."""
    if not confirm:
        console.print("Pass --yes to confirm clearing all tasks."); raise typer.Exit(1)
    save_tasks([]); console.print("🧹 Cleared tasks.")

def app_main():
    app()

if __name__ == "__main__":
    app_main()

Try it locally:

python -m pip install typer[all] rich
python -m tasker_cli add "Buy milk"
python -m tasker_cli list
python -m tasker_cli done 1
python -m tasker_cli list --no-show-done

6) Package & install with entry points

Use a modern pyproject.toml with PEP 621 metadata and a console script entry. We’ll use hatchling as a lightweight build backend:

# pyproject.toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "tasker-cli"
version = "0.1.0"
description = "Tiny to-do manager CLI built with Typer and Rich"
readme = "README.md"
requires-python = ">=3.9"
authors = [{ name = "Your Name" }]
dependencies = ["typer>=0.9", "rich>=13.0"]

[project.scripts]
tasker = "tasker_cli.__main__:app_main"

Install in editable mode while developing:

python -m pip install -e .
# Now just type:
tasker add "Write blog post"
tasker list
How it works: [project.scripts] creates a platform-specific launcher named tasker that calls tasker_cli.__main__:app_main. No manual shebangs or PATH hacks needed.

7) Testing CLIs

Use pytest plus Typer’s test runner to simulate commands and capture output:

# tests/test_tasker.py
from pathlib import Path
from typer.testing import CliRunner
import os
from tasker_cli.__main__ import app

runner = CliRunner()

def test_add_and_list(tmp_path: Path, monkeypatch):
    monkeypatch.setenv("TASKER_FILE", str(tmp_path / "tasks.json"))
    r = runner.invoke(app, ["add", "Buy milk"])
    assert r.exit_code == 0
    r2 = runner.invoke(app, ["list", "--no-show-done"])
    assert "Buy milk" in r2.stdout
python -m pip install pytest
pytest -q

8) Distribute with pipx & PyPI

Install & run globally with pipx

python -m pip install pipx
pipx install .
# or from a git URL:
pipx install git+https://github.com/you/tasker-cli

Publish to PyPI

python -m pip install build twine
python -m build                # creates dist/*.tar.gz and *.whl
python -m twine upload dist/*  # needs PyPI credentials

After publishing, users can install with:

pipx install tasker-cli
# or:
python -m pip install --user tasker-cli

9) Polish: logging, config, shell completion

Logging

import logging, os
level = os.getenv("TASKER_LOG", "WARNING").upper()
logging.basicConfig(level=getattr(logging, level, logging.WARNING))
logging.getLogger("tasker").info("starting up…")

Configuration

Prefer environment variables (12-factor style). For simple files, read from ~/.config/tasker/config.toml on Linux/macOS or %APPDATA%\tasker\config.toml on Windows.

Shell completion

Typer/Click can generate completions:

# Show completion script:
tasker --show-completion

# Install for your shell (Typer adds helpers):
tasker --install-completion

10) Common gotchas

  • Windows paths: Use pathlib.Path, not hardcoded separators.
  • Unicode: Always read/write text files with UTF-8 (Path(...).read_text()/write_text()).
  • State location: Default to a user-writable path (~), not the current directory.
  • Long-running tasks: Show progress or logs; don’t print nothing for minutes.
  • Exit codes: Return non-zero on failure (raise typer.Exit(1)) so scripts can react.

Wrap-up

You’ve seen the full path from a tiny argparse script to a polished, installable CLI with Typer/Click, Rich tables, tests, and packaging. Start small, wire in entry points, then add ergonomics as you go. Happy hacking!

Automating Daily Video Tasks with FFmpeg: A Beginner’s Guide

FFmpeg is a single command-line tool that can convert, compress, trim, merge, subtitle, watermark, and even batch-process videos. This guide gives you copy-pasteable recipes and quick explanations so you can automate your day-to-day work fast.

What is FFmpeg?

FFmpeg is a free, open-source toolkit that works with nearly any audio/video format. If you handle videos—even casually—FFmpeg lets you automate those repetitive chores into single commands or small scripts.

Install & Verify

macOS: brew install ffmpeg (with Homebrew).
Ubuntu/Debian: sudo apt update && sudo apt install ffmpeg.
Windows: Use a prebuilt zip (add its bin folder to PATH).

ffmpeg -version
ffprobe -v error -show_format -show_streams -i input.mp4

Tip: Use ffprobe to inspect codecs, durations, and bitrates before deciding your automation strategy.

Make Web-Ready MP4 (“Fast Start”)

ffmpeg -i input.mov -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k \
-movflags +faststart -pix_fmt yuv420p output.mp4

Compress/Resize for Sharing

ffmpeg -i input.mp4 -vf "scale=-2:1080" -c:v libx264 -crf 24 -preset slow \
-c:a aac -b:a 128k -movflags +faststart -pix_fmt yuv420p output_1080p.mp4

Trim & Clip (Frame-Accurate)

ffmpeg -ss 00:00:05 -to 00:00:12 -i input.mp4 -c:v libx264 -crf 20 -preset medium \
-c:a aac -b:a 128k clip.mp4

Merge/Concatenate Clips

file 'part1.mp4'
file 'part2.mp4'
file 'part3.mp4'
ffmpeg -f concat -safe 0 -i list.txt -c copy merged.mp4

Extract or Replace Audio

ffmpeg -i input.mp4 -vn -c:a libmp3lame -q:a 2 audio.mp3

Thumbnails & GIFs

ffmpeg -ss 00:00:10 -i input.mp4 -vframes 1 -q:v 2 thumb.jpg

Watermarks & Text

ffmpeg -i input.mp4 -i logo.png -filter_complex "overlay=10:10" -c:a copy marked.mp4

Add Subtitles

ffmpeg -i input.mp4 -i subs.srt -c:v copy -c:a copy -c:s mov_text video_with_subs.mp4

Speed Up / Slow Down

ffmpeg -i input.mp4 -filter_complex "setpts=0.5*PTS;atempo=2.0" fast.mp4

Batch Processing

#!/usr/bin/env bash
for f in *.mov; do
  base="${f%.*}"
  ffmpeg -y -i "$f" -vf "scale=-2:1080" -c:v libx264 -crf 23 -preset medium \
  -c:a aac -b:a 128k -movflags +faststart -pix_fmt yuv420p "${base}.mp4"
done

Cheat-Sheet

TaskCommand
Convertffmpeg -i in.mov -c:v libx264 -c:a aac out.mp4
Compress-vf "scale=-2:1080" -crf 24 -preset slow
Trim-ss 00:00:05 -to 00:00:12 -c:v libx264
Extract Audio-vn -c:a libmp3lame -q:a 2 out.mp3

Top 5 Hidden sed Tricks You Didn’t Know Existed

CLI text-processing one-liners

Everyone knows sed for quick find-and-replace, but it can also splice files together in memory, run shell commands, sample lines, and tee matches out to another file. Here are five surprisingly useful tricks—with copy-ready snippets and portability notes for GNU vs. BSD/macOS.

Table of Contents

  1. Run shell commands inside replacements (e flag)
  2. Select every nth line with step addressing
  3. De-duplicate consecutive lines (no uniq needed)
  4. Write matches to a separate file with w
  5. Multi-line editing by “slurping” lines
  6. Cheat sheet

1) Run shell commands inside replacements (e flag)

Scope: GNU sed (Linux, Homebrew gsed on macOS). Executes the right-hand side as a shell command and substitutes its output. Powerful—handle with care.

Use-case: Fill placeholders in a template with dynamic values (date, git hash, hostname, etc.).

# template.txt:
# Build {{DATE}} • Commit {{GIT}} • Host {{HOST}}

sed -E \
  -e 's/\{\{DATE\}\}/date +%F/e' \
  -e 's/\{\{GIT\}\}/git rev-parse --short HEAD/e' \
  -e 's/\{\{HOST\}\}/hostname/e' \
  template.txt
macOS/BSD alternative (no e flag)
# Use the shell to expand variables, then plain sed:
DATE=$(date +%F) GIT=$(git rev-parse --short HEAD) HOST=$(hostname) \
  sed -E "s/\{\{DATE\}\}/$DATE/; s/\{\{GIT\}\}/$GIT/; s/\{\{HOST\}\}/$HOST/" template.txt

2) Select every nth line with step addressing

Use-case: Sample a gigantic log: print every 3rd line (or 10th, etc.).

# Every 3rd line (GNU sed):
sed -n '1~3p' big.log

# Every 10th line:
sed -n '1~10p' big.log
POSIX/BSD sed workaround
# Print 1st, 4th, 7th, ... line (no ~ support):
sed -n '1p; n; n; :a; n; n; n; p; ba' big.log

3) De-duplicate consecutive lines (no uniq needed)

Use-case: Collapse runs of identical adjacent lines to a single line (e.g., noisy logs).

# Input:
# A
# A
# B
# B
# B
# C

sed '$!N; /^\(.*\)\n\1$/!P; D' file.txt
# Output:
# A
# B
# C

4) Write matches to a separate file with w

Use-case: Extract error messages to errors.txt while leaving stdout clean or doing other processing.

# Capture everything after "ERROR: " into errors.txt
sed -nE 's/^ERROR:\s+(.*)$/\1/w errors.txt' server.log

5) Multi-line editing by “slurping” lines

Use-case: Join backslash-continued lines or do a substitution that crosses line breaks.

# Join lines ending with backslash "\" into a single logical line:
sed -e ':a' -e '/\\$/N; s/\\\n//; ta' source.txt

# Replace patterns that span a newline (e.g., "foo\nbar" → "foobar"):
sed ':a;N;$!ba; s/foo\nbar/foobar/g' input.txt

Quick Copy-Paste Cheat Sheet

# 1) Dynamic replacements (GNU sed):
sed -E 's/\{\{DATE\}\}/date +%F/e; s/\{\{GIT\}\}/git rev-parse --short HEAD/e' template.txt

# 2) Every n-th line (GNU sed):
sed -n '1~5p' big.log

# 3) Collapse adjacent duplicates:
sed '$!N; /^\(.*\)\n\1$/!P; D' file.txt

# 4) Write matches to a file:
sed -nE 's/^ERROR:\s+(.*)$/\1/w errors.txt' server.log

# 5) Multi-line “slurp” tricks:
sed -e ':a' -e '/\\$/N; s/\\\n//; ta' source.txt
sed ':a;N;$!ba; s/foo\nbar/foobar/g' input.txt
Pro tip: When a one-liner gets gnarly, save it as a script file and run sed -Ef script.sed input.txt for readability and version control.