Moving from TB 2.x to TB 3.0
What can break when you move a TB 2.x program to TB 3.0, and how to fix it.
Almost every 2.x program runs unchanged on 3.0 and prints the same thing. This page lists what can break and how to fix it. Everything new is described in the "What's new in 3.0" page (https://code.tanisha.ceo/whats-new) and the contract is DESIGN-3.0.md.
1. New reserved words
These words are keywords now and can't be used as variable, function or parameter names:
class self super static extends match const export
from yield async await is enum finally then
TB tells you which one and suggests another name:
Oops! On line 1: 'match' is a keyword in TB 3.0; call it 'found' instead.
Fix: rename the variable (match -> found, from -> source, is -> flag, ...). Dot access is not affected: re.match(...), rec.from, m.class still work, and map keys can be anything.
2. Numbers: whole numbers and fractions are separate
/always divides exactly and gives a fraction (a float). Display is unchanged:6 / 3still prints2and7 / 2prints3.5.- New
//divides and rounds down to a whole number;%is the matching remainder (-7 % 3is2, as before). x.is_int(),x.is_float(),int(x),float(x)tell them apart;type(x)is still"number"for both.
Nothing should break here: a float with no fraction still works as a list index or a range() size (xs[4 / 2], range(6 / 2)), just as in 2.x. Use // when you want a whole number on purpose (xs[len(xs) // 2]).
3. Errors are typed
catch err binds an Error object instead of a plain map. err.message, err.line, err.kind and any extra fields work exactly as before, and so does err["message"].
What changes is err.kind for errors raised by TB itself and by the standard library. It used to be "error" for nearly everything; now it is the snake_case name of the error class:
| Situation | 2.x kind | 3.0 kind (class) |
|---|---|---|
a missing file (fs.read, fs.read_bytes, ...) | error | not_found_error (NotFoundError, a FileError) |
| not allowed to read/write a file | error | permission_error (PermissionError, a FileError) |
| other file problems | error | file_error (FileError) |
| bad JSON / CSV / regex / base64 / hex | error | parse_error (ParseError) |
| http: no connection, DNS failure | error | network_error (NetworkError) |
| http: no answer in time | error | timeout_error (TimeoutError) |
a library given the wrong type (fs.read(5)) | error | type_error (TypeError) |
"a" - 1, -"a", calling a non-function | error | type_error (TypeError) |
int("abc"), num("abc"), dividing by zero | error | value_error (ValueError) |
assert failing | - | assertion_error (AssertionError) |
Fix: if you compared err.kind == "error", catch by class instead: catch err: FileError { }, or test err is Error. Your own throw "text" and throw {"message": ...} still give kind "error" (or the kind you put in the map).
4. list * n repeats the list
[0] * 3 is now [0, 0, 0] (like text: "ab" * 2). In 2.x it was an error. [1] * [2] now says a list can only be repeated a whole number of times.
5. Two loop variables over a map give key and value
for k, v in m { } iterates pairs; for k in m { } still gives keys only, as in 2.x.
6. type() knows more kinds
type(x) can now also give "bytes", "range", "generator", "class", "enum", an enum's name (for its values) or a class name (for objects). Code that assumed type() only gives the 2.x names should add a branch.
7. Text and bytes literals
b"...",r"..."andf"..."(andrb,rf) are literal prefixes. A variable namedb,rorffollowed directly by text without an operator was already a syntax error, so nothing breaks.- Escapes
\0 \xNN \u{HEX} \'are new; other unknown escapes like\qstill keep the backslash. """..."""is a multi-line text literal.
8. Library changes
test.results()has a"skipped"count;test.raises(fn, part)still works andtest.raises(fn, ErrorClass, part?)is new.hash.md5/sha1/sha256/sha512/hmac_sha256/compareaccept bytes as well as text; the wrong-type message says "text or bytes".- Native (Python) libraries:
fail()takes an optionalcls=and there arefail_type/fail_value/fail_file/fail_parse/fail_network/fail_permission/ fail_not_found/fail_timeouthelpers;check_typeknows"bytes";native_class/method/make_objectbuild objects with methods and operators (seetblib/_api.py). decimalvalues areDecimalobjects now (they were maps): operators work (decimal.of("0.1") + decimal.of("0.2") == decimal.of("0.3")) andtype(d)is"Decimal";.text,.numberand every method are as before.
9. Embedding TB from Python
- The source is split into
tb.pyplus thetbcore/package; bundlers must shiptbcore/next totb.py(file list intbcore/__init__.py).import tbstill gives the same public API. Interpreter.frames()is a method (the debugger API: list of{fn, line, file, locals}); the internal frame list isInterpreter.call_stack.Interpreter.trace = fn(event, line, file, env)andInterpreter.eval(source, env)are new; tokens carrycomments_before(with""for blank lines between comments),blank_lines_beforeandtrailing_comment.