Skip to content

Dictionary access conversion

This feature bundles three related intentions that help you switch between the most common dictionary lookup styles depending on whether you want terse code, explicit fallback handling, or exception-driven control flow.

All three intentions only activate for expressions BetterPy can verify as mappings. That keeps the feature from appearing on unrelated subscription syntax such as lists, tuples, or arbitrary objects that just happen to define a get() method.

Convert bracket access to .get()

How to invoke: ⌥⏎ / Alt+Enter on mapping[key]"BetterPy: Replace 'dict[key]' with 'dict.get(key)'"

Use this when a missing key should quietly yield None instead of raising KeyError.

timeout = config["timeout"]

becomes:

timeout = config.get("timeout")

What it supports

  • Ordinary dictionary and mapping lookups such as payload["id"].
  • Variable keys like payload[key_name].
  • Tuple keys, including the comma form mapping[a, b], which is rewritten to mapping.get((a, b)).
  • Complex mapping expressions such as factory().cache["users"].
  • Inline comments and spacing inside the brackets; BetterPy preserves the bracket contents instead of rebuilding them from scratch.

What it deliberately avoids

  • Assignment targets like mapping[key] = value.
  • Deletions such as del mapping[key].
  • Augmented assignments like mapping[key] += 1.
  • Non-mapping operands such as lists.

Those cases are skipped because .get() changes the meaning: it reads a value, but it cannot stand in for write/delete syntax.

Convert .get() to bracket access

How to invoke: ⌥⏎ / Alt+Enter on mapping.get(key)"BetterPy: Replace 'dict.get(key)' with 'dict[key]'"

Use this when a key is expected to exist and surfacing KeyError is preferable to silently returning None.

username = payload.get("username")

becomes:

username = payload["username"]

What it supports

  • One-argument .get() calls.
  • Positional keys such as mapping.get(key).
  • Keyword style calls like mapping.get(key=name), which become mapping[name].

What it deliberately avoids

  • .get() calls with a default, for example mapping.get("timeout", 30).
  • Calls with zero arguments or more than one effective argument.
  • Calls on non-mapping values.
  • Shadowed or unrelated get call sites where BetterPy cannot prove the qualifier is a mapping lookup.

This restriction matters because mapping.get(key, default) is not equivalent to mapping[key]; replacing it would drop the fallback value.

Convert .get(..., default) to try / except KeyError

How to invoke: ⌥⏎ / Alt+Enter on mapping.get(key, default) used as a full assignment or return value → "BetterPy: Replace 'dict.get(key, default)' with try/except KeyError"

Use this when you want explicit exception-based control flow while keeping the same fallback result.

result = payload.get("name", "unknown")

becomes:

try:
    result = payload["name"]
except KeyError:
    result = "unknown"

It also works for direct returns:

return payload.get("name", "unknown")

becomes:

try:
    return payload["name"]
except KeyError:
    return "unknown"

What it supports

  • Full assignment statements with exactly one target.
  • Full return statements.
  • Positional and keyword forms for key and default.
  • Safe default expressions such as literals, constants, references, and simple constant-style subscriptions.

What it deliberately avoids

  • Nested or partial expressions like foo(payload.get("name", "unknown")).
  • Assignments with multiple targets.
  • Calls without a default value.
  • Defaults that may have side effects or are expensive to duplicate.

The generated try block repeats the lookup once and the fallback once, so BetterPy only offers the intention when that duplication is predictable and safe.

Convert try / except KeyError to .get()

How to invoke: ⌥⏎ / Alt+Enter inside a qualifying try block → "BetterPy: Replace try-except with dict.get"

Use this when explicit exception handling is unnecessary and a compact lookup reads better.

try:
    result = payload["name"]
except KeyError:
    result = "unknown"

becomes:

result = payload.get("name", "unknown")

It also supports the common initialization-plus-pass pattern:

result = "unknown"
try:
    result = payload["name"]
except KeyError:
    pass

becomes:

result = payload.get("name", "unknown")

What it supports

  • A single except KeyError clause.
  • A try block containing exactly one statement.
  • Assignment or return forms.
  • Fallbacks expressed as a literal or simple reference.
  • Tuple keys and parenthesized complex mapping expressions.

What it deliberately avoids

  • try statements with else or finally blocks.
  • Multiple except clauses.
  • Nested subscriptions such as mapping[a][b], where .get() would not preserve the same failure boundary.
  • Complex fallback expressions.

These constraints ensure the shorter .get() form stays semantically equivalent to the original exception-handling code.

When to prefer each form

  • Use mapping[key] when the key is required and a missing value should fail loudly.
  • Use mapping.get(key) when absence is expected and None is a meaningful signal.
  • Use mapping.get(key, default) when you want a compact fallback.
  • Use try / except KeyError when the fallback path needs to be visually explicit or is about to grow into more than a single expression.

Because BetterPy supports moving in both directions, you can start with the clearest form for the current situation and change it later without rewriting the statement by hand.