nilpy: fallback import (try/except ImportError)
- Type: feature (Nil-Python frontend, import system) — Track N
- Status: done
- Opened: 2026-07-25 — songformatter planning session. Replaces the earlier
feature-nilpy-dotted-from-import(Rene: don't impersonate reportlab; give nilpy the honest portability primitive instead). See [[frank2-songformatter-pxx-target]].
Motivation
try: import X … except ImportError: import Y is the single most common
real-world Python portability idiom — every serious package uses it for
optional/alternative deps. Supporting it makes far more real-world Python compile
under nilpy (push generality down — [[frank2-mission-compile-real-world-asis]]),
and it is the honest mechanism for platform-split backends:
try:
from reportlab.pdfgen import canvas # cpython: real reportlab
except ImportError:
from pxxpdf import canvas # nilpy: pxx pdfgen-backed compat lib
Under cpython the first import wins. Under nilpy reportlab is unresolvable, so
nilpy must soft-fail that import and compile the except branch — instead of
today's hard error. Consumer code downstream is identical (both bind canvas),
which is why [[feature-lib-pxxpdf-reportlab-compat]] mimics reportlab's API.
Rene also floated a short import X if not import Y form — decide whether to add
sugar or just support the try/except ImportError shape (the latter is standard
Python and preferred).
Current walls
import→tkUsesat LEX time (compiler/pylexer.inc:49) — eager, branch-agnostic; no compile-time pruning, no__nilpy__/sys.implementationmarker.from X import …accepted ONLY for dataclasses/typing/itertools; else hard error'Nil Python: from-import of module … is not supported yet'(compiler/pyparser.inc:8761-8783).- Bare
import namealready resolves a sibling unit (ParseUsesUnit,pyparser.inc:8799) — soimport pxxpdf(a sibling module) works once the fallback lets us reach it.
Intended surface
- Recognize
try: <import(s)> except ImportError: <import(s)>at module scope. - Attempt the try-branch imports; on unresolvable module (not a runtime error — a compile-time resolution miss), discard that branch and compile the except-branch imports instead. No hard error for the skipped module.
- Must PARSE (consume) a dotted
from a.b import cin the try-branch even though it will be skipped under nilpy — do NOT need to RESOLVEa.b(we never load reportlab). Parsing-to-skip only. - Bind the names from whichever branch was taken.
Acceptance
- A test:
try: from nonexistent.pkg import thing except ImportError: from realmod import thingcompiles under nilpy, bindsthingfromrealmod, runs. - The reportlab→pxxpdf fallback compiles (verified by [[feature-lib-pxxpdf-reportlab-compat]]).
- Existing
make test-nilpygreen + self-host byte-identical. - Under cpython the same source still prefers the try-branch (semantics unchanged there — it's real Python).
Consider (Track U, not here)
A sys.implementation.name == 'nilpy' marker would enable an explicit
if nilpy: split as an alternative to try/except. Out of scope; file a decide-*
if wanted (try/except is the standard idiom and covers the need).
Log
- 2026-07-25 — filed, replacing feature-nilpy-dotted-from-import.
- 2026-07-31 — already landed and gated (no landing entry was recorded
here):
test/test_nilpy_fallback_import.npyandtest/test_nilpy_fallback_import_try_wins.npycover both directions ("try wins" and "fallback wins"), already in the Makefile ("try/except ImportError picks a branch at COMPILE time, both directions"). Re-verified directly against the ticket's own reportlab->fallback shape: both directions compile and bind correctly.--no-shims(mentioned in the sibling dotted-imports ticket) also already exists and is gated. Closing — nothing left open. - 2026-07-31 — resolved, commit dcdb77a69.