pxt-core 7.5.43 → 7.5.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/built/pxtpy.js CHANGED
@@ -241,11 +241,19 @@ var pxt;
241
241
  return pxt.U.lookup(internalApis, name) || pxt.U.lookup(externalApis, name);
242
242
  }
243
243
  function lookupGlobalSymbol(name) {
244
+ var _a;
244
245
  if (!name)
245
246
  return undefined;
246
247
  let sym = lookupApi(name);
247
248
  if (sym)
248
249
  getOrSetSymbolType(sym);
250
+ else if (name.indexOf(".") && !name.endsWith(".__constructor")) {
251
+ const base = name.substring(0, name.lastIndexOf("."));
252
+ const baseSymbol = lookupGlobalSymbol(base);
253
+ if ((baseSymbol === null || baseSymbol === void 0 ? void 0 : baseSymbol.kind) === 8 /* Class */ && ((_a = baseSymbol.extendsTypes) === null || _a === void 0 ? void 0 : _a.length)) {
254
+ return lookupGlobalSymbol(baseSymbol.extendsTypes[0] + name.substring(base.length));
255
+ }
256
+ }
249
257
  return sym;
250
258
  }
251
259
  function initApis(apisInfo, tsShadowFiles) {
@@ -328,7 +336,10 @@ var pxt;
328
336
  pref += ".";
329
337
  }
330
338
  let qualifiedName = pref + name;
331
- if (isLocalScope(scope)
339
+ if (scope.kind === "ClassDef") {
340
+ varSym = addSymbol(2 /* Property */, qualifiedName);
341
+ }
342
+ else if (isLocalScope(scope)
332
343
  && (modifier === py_1.VarModifier.Global
333
344
  || modifier === py_1.VarModifier.NonLocal)) {
334
345
  varSym = addSymbol(4 /* Variable */, name);
@@ -442,7 +453,7 @@ var pxt;
442
453
  };
443
454
  }
444
455
  }
445
- // next free error 9575
456
+ // next free error 9576
446
457
  function error(astNode, code, msg) {
447
458
  diagnostics.push(mkDiag(astNode, pxtc.DiagnosticCategory.Error, code, msg));
448
459
  //const pos = position(astNode ? astNode.startPos || 0 : 0, mod.source)
@@ -657,8 +668,19 @@ var pxt;
657
668
  let qn = cd.symInfo.qName;
658
669
  return pxt.U.values(internalApis).filter(e => e.namespace == qn && e.kind == 2 /* Property */);
659
670
  }
660
- function getClassField(ct, n, checkOnly = false, skipBases = false) {
661
- let qid = ct.pyQName + "." + n;
671
+ function getClassField(ct, n, isStatic, checkOnly = false, skipBases = false) {
672
+ let qid;
673
+ if (n === "__init__") {
674
+ qid = ct.pyQName + ".__constructor";
675
+ }
676
+ else {
677
+ if (n.startsWith(ct.pyQName + ".")) {
678
+ qid = n;
679
+ }
680
+ else {
681
+ qid = ct.pyQName + "." + n;
682
+ }
683
+ }
662
684
  let f = lookupGlobalSymbol(qid);
663
685
  if (f)
664
686
  return f;
@@ -668,7 +690,7 @@ var pxt;
668
690
  if (sym) {
669
691
  if (sym == ct)
670
692
  pxt.U.userError("field lookup loop on: " + sym.qName + " / " + n);
671
- let classF = getClassField(sym, n, true);
693
+ let classF = getClassField(sym, n, isStatic, true);
672
694
  if (classF)
673
695
  return classF;
674
696
  }
@@ -676,7 +698,7 @@ var pxt;
676
698
  }
677
699
  if (!checkOnly && ct.pyAST && ct.pyAST.kind == "ClassDef") {
678
700
  let sym = addSymbol(2 /* Property */, qid);
679
- sym.isInstance = true;
701
+ sym.isInstance = !isStatic;
680
702
  return sym;
681
703
  }
682
704
  return null;
@@ -693,16 +715,14 @@ var pxt;
693
715
  const recvType = typeOf(recv);
694
716
  const constructorTypes = getTypesForFieldLookup(recvType);
695
717
  for (let ct of constructorTypes) {
696
- let f = getClassField(ct, n, checkOnly);
718
+ let isModule = !!recvType.moduleType;
719
+ let f = getClassField(ct, n, isModule, checkOnly);
697
720
  if (f) {
698
- let isModule = !!recvType.moduleType;
699
721
  if (isModule) {
700
722
  if (f.isInstance)
701
723
  error(null, 9505, pxt.U.lf("the field '{0}' of '{1}' is not static", n, ct.pyQName));
702
724
  }
703
725
  else {
704
- if (!f.isInstance)
705
- error(null, 9504, pxt.U.lf("the field '{0}' of '{1}' is static", n, ct.pyQName));
706
726
  if (isSuper(recv))
707
727
  f.isProtected = true;
708
728
  else if (isThis(recv)) {
@@ -885,14 +905,17 @@ var pxt;
885
905
  return mkType({});
886
906
  }
887
907
  function doArgs(n, isMethod) {
908
+ var _a;
888
909
  const args = n.args;
889
910
  if (args.kwonlyargs.length)
890
911
  error(n, 9517, pxt.U.lf("keyword-only arguments not supported yet"));
891
912
  let nargs = args.args.slice();
892
913
  if (isMethod) {
893
- if (nargs[0].arg != "self")
894
- error(n, 9518, pxt.U.lf("first argument of method has to be called 'self'"));
895
- nargs.shift();
914
+ if (((_a = nargs[0]) === null || _a === void 0 ? void 0 : _a.arg) !== "self")
915
+ n.symInfo.isStatic = true;
916
+ else {
917
+ nargs.shift();
918
+ }
896
919
  }
897
920
  else {
898
921
  if (nargs.some(a => a.arg == "self"))
@@ -1054,6 +1077,7 @@ var pxt;
1054
1077
  }
1055
1078
  function emitFunctionDef(n, inline = false) {
1056
1079
  return guardedScope(n, () => {
1080
+ var _a, _b, _c, _d;
1057
1081
  const isMethod = !!ctx.currClass && !ctx.currFun;
1058
1082
  const topLev = isTopLevel();
1059
1083
  const nested = !!ctx.currFun;
@@ -1106,7 +1130,7 @@ var pxt;
1106
1130
  let scopeValueVar = n.vars["value"];
1107
1131
  let valueVar = scopeValueVar === null || scopeValueVar === void 0 ? void 0 : scopeValueVar.symbol;
1108
1132
  if (funname == "__set__" && valueVar) {
1109
- let cf = getClassField(ctx.currClass.symInfo, "__get__");
1133
+ let cf = getClassField(ctx.currClass.symInfo, "__get__", false);
1110
1134
  if (cf && cf.pyAST && cf.pyAST.kind == "FunctionDef")
1111
1135
  unify(n, valueVar.pyRetType, cf.pyRetType);
1112
1136
  }
@@ -1114,6 +1138,9 @@ var pxt;
1114
1138
  }
1115
1139
  if (!prefix) {
1116
1140
  prefix = funname[0] == "_" ? (sym.isProtected ? "protected" : "private") : "public";
1141
+ if (n.symInfo.isStatic) {
1142
+ prefix += " static";
1143
+ }
1117
1144
  }
1118
1145
  nodes.push(B.mkText(prefix + " "), quote(funname));
1119
1146
  }
@@ -1125,7 +1152,7 @@ var pxt;
1125
1152
  else
1126
1153
  nodes.push(B.mkText("export function "), quote(funname));
1127
1154
  }
1128
- let retType = n.returns ? compileType(n.returns) : sym.pyRetType;
1155
+ let retType = n.name == "__init__" ? undefined : (n.returns ? compileType(n.returns) : sym.pyRetType);
1129
1156
  nodes.push(doArgs(n, isMethod), retType && canonicalize(retType) != tpVoid ? typeAnnot(retType) : B.mkText(""));
1130
1157
  // make sure type is initialized
1131
1158
  getOrSetSymbolType(sym);
@@ -1133,6 +1160,13 @@ var pxt;
1133
1160
  if (n.name == "__init__") {
1134
1161
  if (!ctx.currClass)
1135
1162
  error(n, 9533, lf("__init__ method '{0}' is missing current class context", sym.pyQName));
1163
+ if ((_a = ctx.currClass) === null || _a === void 0 ? void 0 : _a.baseClass) {
1164
+ const firstStatement = n.body[0];
1165
+ const superConstructor = ctx.currClass.baseClass.pyQName + ".__constructor";
1166
+ if (((_d = (_c = (_b = firstStatement.value) === null || _b === void 0 ? void 0 : _b.func) === null || _c === void 0 ? void 0 : _c.symbolInfo) === null || _d === void 0 ? void 0 : _d.pyQName) !== superConstructor) {
1167
+ error(n, 9575, lf("Sub classes must call 'super().__init__' as the first statement inside an __init__ method"));
1168
+ }
1169
+ }
1136
1170
  for (let f of listClassFields(ctx.currClass)) {
1137
1171
  let p = f.pyAST;
1138
1172
  if (p && p.value) {
@@ -1176,22 +1210,61 @@ var pxt;
1176
1210
  nodes.push(B.mkCommaSep(n.bases.map(expr)));
1177
1211
  let b = getClassDef(n.bases[0]);
1178
1212
  if (b) {
1179
- n.baseClass = b;
1213
+ n.baseClass = b.symInfo;
1180
1214
  sym.extendsTypes = [b.symInfo.pyQName];
1181
1215
  }
1216
+ else {
1217
+ const nm = tryGetName(n.bases[0]);
1218
+ if (nm) {
1219
+ const localSym = lookupSymbol(nm);
1220
+ const globalSym = lookupGlobalSymbol(nm);
1221
+ n.baseClass = localSym || globalSym;
1222
+ if (n.baseClass)
1223
+ sym.extendsTypes = [n.baseClass.pyQName];
1224
+ }
1225
+ }
1182
1226
  }
1183
1227
  }
1184
- let body = stmts(n.body);
1228
+ const classDefs = n.body.filter(s => n.isNamespace || s.kind === "FunctionDef");
1229
+ const staticStmts = n.isNamespace ? [] : n.body.filter(s => classDefs.indexOf(s) === -1 && s.kind !== "Pass");
1230
+ let body = stmts(classDefs);
1185
1231
  nodes.push(body);
1186
- let fieldDefs = listClassFields(n)
1187
- .filter(f => f.kind == 2 /* Property */ && f.isInstance)
1188
- .map(f => {
1189
- if (!f.pyName || !f.pyRetType)
1190
- error(n, 9535, lf("field definition missing py name or ret type", f.qName));
1191
- return f;
1192
- })
1193
- .map((f) => B.mkStmt(accessAnnot(f), quote(f.pyName), typeAnnot(f.pyRetType)));
1194
- body.children = fieldDefs.concat(body.children);
1232
+ // Python classes allow arbitrary statements in their bodies, sort of like namespaces.
1233
+ // Take all of these statements and put them in a static method that we can call when
1234
+ // the class is defined.
1235
+ let generatedInitFunction = false;
1236
+ if (staticStmts.length) {
1237
+ generatedInitFunction = true;
1238
+ const staticBody = stmts(staticStmts);
1239
+ const initFun = B.mkStmt(B.mkGroup([
1240
+ B.mkText(`public static __init${n.name}() `),
1241
+ staticBody
1242
+ ]));
1243
+ body.children.unshift(initFun);
1244
+ }
1245
+ if (!n.isNamespace) {
1246
+ const fieldDefs = listClassFields(n)
1247
+ .map(f => {
1248
+ if (!f.pyName || !f.pyRetType)
1249
+ error(n, 9535, lf("field definition missing py name or return type", f.qName));
1250
+ return f;
1251
+ });
1252
+ const staticFieldSymbols = fieldDefs.filter(f => !f.isInstance);
1253
+ const instanceFields = fieldDefs.filter(f => f.isInstance)
1254
+ .map((f) => B.mkStmt(accessAnnot(f), quote(f.pyName), typeAnnot(f.pyRetType)));
1255
+ const staticFields = staticFieldSymbols
1256
+ .map((f) => B.mkGroup([
1257
+ B.mkStmt(accessAnnot(f), B.mkText("static "), quote(f.pyName), typeAnnot(f.pyRetType)),
1258
+ declareLocalStatic(quoteStr(n.name), quoteStr(f.pyName), t2s(f.pyRetType))
1259
+ ]));
1260
+ body.children = staticFields.concat(instanceFields).concat(body.children);
1261
+ }
1262
+ if (generatedInitFunction) {
1263
+ nodes = [
1264
+ B.mkStmt(B.mkGroup(nodes)),
1265
+ B.mkStmt(B.mkText(`${n.name}.__init${n.name}()`))
1266
+ ];
1267
+ }
1195
1268
  return B.mkStmt(B.mkGroup(nodes));
1196
1269
  }),
1197
1270
  Return: (n) => {
@@ -1498,7 +1571,7 @@ var pxt;
1498
1571
  // class fields can't be const
1499
1572
  // hack: value in @namespace should always be const
1500
1573
  isConstCall = !!(value && ctx.currClass.isNamespace);
1501
- let fd = getClassField(ctx.currClass.symInfo, nm);
1574
+ let fd = getClassField(ctx.currClass.symInfo, nm, true);
1502
1575
  if (!fd)
1503
1576
  error(n, 9544, lf("cannot get class field"));
1504
1577
  // TODO: use or remove this code
@@ -1540,7 +1613,9 @@ var pxt;
1540
1613
  error(n, 9539, lf("function '{0}' missing return type", fd.pyQName));
1541
1614
  unifyTypeOf(target, fd.pyRetType);
1542
1615
  fd.isInstance = false;
1543
- pref = ctx.currClass.isNamespace ? `export ${isConstCall ? "const" : "let"} ` : "static ";
1616
+ if (ctx.currClass.isNamespace) {
1617
+ pref = `export ${isConstCall ? "const" : "let"} `;
1618
+ }
1544
1619
  }
1545
1620
  if (value)
1546
1621
  unifyTypeOf(target, typeOf(value));
@@ -1606,6 +1681,7 @@ var pxt;
1606
1681
  }
1607
1682
  }
1608
1683
  function possibleDef(n, excludeLet = false) {
1684
+ var _a, _b;
1609
1685
  let id = n.id;
1610
1686
  let currScopeVar = lookupScopeSymbol(id);
1611
1687
  let curr = currScopeVar === null || currScopeVar === void 0 ? void 0 : currScopeVar.symbol;
@@ -1640,6 +1716,10 @@ var pxt;
1640
1716
  if (n.isdef && !excludeLet) {
1641
1717
  return B.mkGroup([B.mkText("let "), quote(id)]);
1642
1718
  }
1719
+ else if ((curr === null || curr === void 0 ? void 0 : curr.namespace) && (curr === null || curr === void 0 ? void 0 : curr.qName) && !(((_a = ctx.currClass) === null || _a === void 0 ? void 0 : _a.isNamespace) && ((_b = ctx.currClass) === null || _b === void 0 ? void 0 : _b.name) === (curr === null || curr === void 0 ? void 0 : curr.namespace))) {
1720
+ // If this is a static variable in a class, we want the full qname
1721
+ return quote(curr.qName);
1722
+ }
1643
1723
  else
1644
1724
  return quote(id);
1645
1725
  }
@@ -1653,20 +1733,27 @@ var pxt;
1653
1733
  //return id.replace(/([a-z0-9])_([a-zA-Z0-9])/g, (f: string, x: string, y: string) => x + y.toUpperCase())
1654
1734
  }
1655
1735
  function tryGetName(e) {
1736
+ var _a;
1656
1737
  if (e.kind == "Name") {
1657
1738
  let s = e.id;
1658
1739
  let scopeV = lookupVar(s);
1659
1740
  let v = scopeV === null || scopeV === void 0 ? void 0 : scopeV.symbol;
1660
- if (v && v.expandsTo)
1661
- return v.expandsTo;
1662
- else
1663
- return s;
1741
+ if (v) {
1742
+ if (v.expandsTo)
1743
+ return v.expandsTo;
1744
+ else if (ctx.currClass && !ctx.currFun && !(scopeV === null || scopeV === void 0 ? void 0 : scopeV.modifier) && v.qName)
1745
+ return v.qName;
1746
+ }
1747
+ return s;
1664
1748
  }
1665
1749
  if (e.kind == "Attribute") {
1666
1750
  let pref = tryGetName(e.value);
1667
1751
  if (pref)
1668
1752
  return pref + "." + e.attr;
1669
1753
  }
1754
+ if (isSuper(e) && ((_a = ctx.currClass) === null || _a === void 0 ? void 0 : _a.baseClass)) {
1755
+ return ctx.currClass.baseClass.qName;
1756
+ }
1670
1757
  return undefined;
1671
1758
  }
1672
1759
  function getName(e) {
@@ -1874,6 +1961,7 @@ var pxt;
1874
1961
  return r;
1875
1962
  },
1876
1963
  Call: (n) => {
1964
+ var _a, _b, _c, _d, _e;
1877
1965
  // TODO(dz): move body out; needs seperate PR that doesn't touch content
1878
1966
  n.func.inCalledPosition = true;
1879
1967
  let nm = tryGetName(n.func);
@@ -1885,6 +1973,9 @@ var pxt;
1885
1973
  let methName = "";
1886
1974
  if (isClass) {
1887
1975
  fun = lookupSymbol(namedSymbol.pyQName + ".__constructor");
1976
+ if (!fun) {
1977
+ fun = addSymbolFor(3 /* Function */, createDummyConstructorSymbol(namedSymbol === null || namedSymbol === void 0 ? void 0 : namedSymbol.pyAST));
1978
+ }
1888
1979
  }
1889
1980
  else {
1890
1981
  if (n.func.kind == "Attribute") {
@@ -1904,7 +1995,7 @@ var pxt;
1904
1995
  if (ctx.currClass && ctx.currClass.baseClass) {
1905
1996
  if (!n.tsType)
1906
1997
  error(n, 9543, lf("call expr missing ts type"));
1907
- unifyClass(n, n.tsType, ctx.currClass.baseClass.symInfo);
1998
+ unifyClass(n, n.tsType, ctx.currClass.baseClass);
1908
1999
  }
1909
2000
  return B.mkText("super");
1910
2001
  }
@@ -1964,6 +2055,17 @@ var pxt;
1964
2055
  unify(n, n.tsType, tpString);
1965
2056
  return B.mkInfix(B.mkText(`""`), "+", expr(n.args[0]));
1966
2057
  }
2058
+ const isSuperAttribute = n.func.kind === "Attribute" && isSuper(n.func.value);
2059
+ if (!fun && isSuperAttribute) {
2060
+ fun = lookupGlobalSymbol(nm);
2061
+ }
2062
+ const isSuperConstructor = ((_a = ctx.currFun) === null || _a === void 0 ? void 0 : _a.name) === "__init__" &&
2063
+ (fun === null || fun === void 0 ? void 0 : fun.name) === "__constructor" &&
2064
+ ((_c = (_b = ctx.currClass) === null || _b === void 0 ? void 0 : _b.baseClass) === null || _c === void 0 ? void 0 : _c.pyQName) === (fun === null || fun === void 0 ? void 0 : fun.namespace) &&
2065
+ isSuperAttribute;
2066
+ if (isSuperConstructor) {
2067
+ fun = lookupSymbol(((_e = (_d = ctx.currClass) === null || _d === void 0 ? void 0 : _d.baseClass) === null || _e === void 0 ? void 0 : _e.pyQName) + ".__constructor");
2068
+ }
1967
2069
  if (!fun) {
1968
2070
  error(n, 9508, pxt.U.lf("can't find called function '{0}'", nm));
1969
2071
  }
@@ -2071,7 +2173,13 @@ var pxt;
2071
2173
  ]);
2072
2174
  }
2073
2175
  }
2074
- let fn = methName ? B.mkInfix(expr(recv), ".", B.mkText(methName)) : expr(n.func);
2176
+ let fn;
2177
+ if (isSuperConstructor) {
2178
+ fn = B.mkText("super");
2179
+ }
2180
+ else {
2181
+ fn = methName ? B.mkInfix(expr(recv), ".", B.mkText(methName)) : expr(n.func);
2182
+ }
2075
2183
  let nodes = [
2076
2184
  fn,
2077
2185
  B.mkText("("),
@@ -2728,6 +2836,68 @@ var pxt;
2728
2836
  break;
2729
2837
  }
2730
2838
  }
2839
+ function createDummyConstructorSymbol(def, sym = def.symInfo) {
2840
+ var _a;
2841
+ const existing = lookupApi(sym.pyQName + ".__constructor");
2842
+ if (!existing && ((_a = sym.extendsTypes) === null || _a === void 0 ? void 0 : _a.length)) {
2843
+ const parentSymbol = lookupSymbol(sym.extendsTypes[0]) || lookupGlobalSymbol(sym.extendsTypes[0]);
2844
+ if (parentSymbol) {
2845
+ return createDummyConstructorSymbol(def, parentSymbol);
2846
+ }
2847
+ }
2848
+ const result = {
2849
+ kind: "FunctionDef",
2850
+ name: "__init__",
2851
+ startPos: def.startPos,
2852
+ endPos: def.endPos,
2853
+ parent: def,
2854
+ body: [],
2855
+ args: {
2856
+ kind: "Arguments",
2857
+ startPos: 0,
2858
+ endPos: 0,
2859
+ args: [{
2860
+ startPos: 0,
2861
+ endPos: 0,
2862
+ kind: "Arg",
2863
+ arg: "self"
2864
+ }],
2865
+ kw_defaults: [],
2866
+ kwonlyargs: [],
2867
+ defaults: []
2868
+ },
2869
+ decorator_list: [],
2870
+ vars: {},
2871
+ symInfo: mkSymbol(3 /* Function */, def.symInfo.qName + ".__constructor")
2872
+ };
2873
+ result.symInfo.parameters = [];
2874
+ result.symInfo.pyRetType = mkType({ classType: def.symInfo });
2875
+ if (existing) {
2876
+ result.args.args.push(...existing.parameters.map(p => ({
2877
+ startPos: 0,
2878
+ endPos: 0,
2879
+ kind: "Arg",
2880
+ arg: p.name,
2881
+ })));
2882
+ result.symInfo.parameters.push(...existing.parameters.map(p => {
2883
+ if (p.pyType)
2884
+ return p;
2885
+ const res = Object.assign(Object.assign({}, p), { pyType: mapTsType(p.type) });
2886
+ return res;
2887
+ }));
2888
+ }
2889
+ return result;
2890
+ }
2891
+ function declareLocalStatic(className, name, type) {
2892
+ const isSetVar = `___${name}_is_set`;
2893
+ const localVar = `___${name}`;
2894
+ return B.mkStmt(B.mkStmt(B.mkText(`private ${isSetVar}: boolean`)), B.mkStmt(B.mkText(`private ${localVar}: ${type}`)), B.mkStmt(B.mkText(`get ${name}(): ${type}`), B.mkBlock([
2895
+ B.mkText(`return this.${isSetVar} ? this.${localVar} : ${className}.${name}`)
2896
+ ])), B.mkStmt(B.mkText(`set ${name}(value: ${type})`), B.mkBlock([
2897
+ B.mkStmt(B.mkText(`this.${isSetVar} = true`)),
2898
+ B.mkStmt(B.mkText(`this.${localVar} = value`)),
2899
+ ])));
2900
+ }
2731
2901
  })(py = pxt.py || (pxt.py = {}));
2732
2902
  })(pxt || (pxt = {}));
2733
2903
  // Lexer spec: https://docs.python.org/3/reference/lexical_analysis.html
@@ -107,7 +107,8 @@ declare namespace pxt.runner {
107
107
  function buildSimJsInfo(simOptions: SimulateOptions): Promise<pxtc.BuiltSimJsInfo>;
108
108
  enum LanguageMode {
109
109
  Blocks = 0,
110
- TypeScript = 1
110
+ TypeScript = 1,
111
+ Python = 2
111
112
  }
112
113
  let editorLanguageMode: LanguageMode;
113
114
  function setEditorContextAsync(mode: LanguageMode, localeInfo: string): Promise<void>;
@@ -1845,6 +1845,7 @@ var pxt;
1845
1845
  (function (LanguageMode) {
1846
1846
  LanguageMode[LanguageMode["Blocks"] = 0] = "Blocks";
1847
1847
  LanguageMode[LanguageMode["TypeScript"] = 1] = "TypeScript";
1848
+ LanguageMode[LanguageMode["Python"] = 2] = "Python";
1848
1849
  })(LanguageMode = runner.LanguageMode || (runner.LanguageMode = {}));
1849
1850
  runner.editorLanguageMode = LanguageMode.Blocks;
1850
1851
  function setEditorContextAsync(mode, localeInfo) {
@@ -1874,7 +1875,14 @@ var pxt;
1874
1875
  case "fileloaded":
1875
1876
  let fm = m;
1876
1877
  let name = fm.name;
1877
- setEditorContextAsync(/\.ts$/i.test(name) ? LanguageMode.TypeScript : LanguageMode.Blocks, fm.locale);
1878
+ let mode = LanguageMode.Blocks;
1879
+ if (/\.ts$/i.test(name)) {
1880
+ mode = LanguageMode.TypeScript;
1881
+ }
1882
+ else if (/\.py$/i.test(name)) {
1883
+ mode = LanguageMode.Python;
1884
+ }
1885
+ setEditorContextAsync(mode, fm.locale);
1878
1886
  break;
1879
1887
  case "popout":
1880
1888
  let mp = /((\/v[0-9+])\/)?[^\/]*#(doc|md):([^&?:]+)/i.exec(window.location.href);
@@ -2072,24 +2080,33 @@ var pxt;
2072
2080
  el.setAttribute("aria-disabled", "false");
2073
2081
  }
2074
2082
  }
2075
- function renderHash() {
2083
+ async function renderHashAsync() {
2076
2084
  let m = /^#(doc|md|tutorial|book|project|projectid|print):([^&?:]+)(:([^&?:]+):([^&?:]+))?/i.exec(window.location.hash);
2077
2085
  if (m) {
2078
2086
  pushHistory();
2087
+ if (m[4]) {
2088
+ let mode = LanguageMode.TypeScript;
2089
+ if (/^blocks$/i.test(m[4])) {
2090
+ mode = LanguageMode.Blocks;
2091
+ }
2092
+ else if (/^python$/i.test(m[4])) {
2093
+ mode = LanguageMode.Python;
2094
+ }
2095
+ await setEditorContextAsync(mode, m[5]);
2096
+ }
2079
2097
  // navigation occured
2080
- const p = m[4] ? setEditorContextAsync(/^blocks$/.test(m[4]) ? LanguageMode.Blocks : LanguageMode.TypeScript, m[5]) : Promise.resolve();
2081
- p.then(() => render(m[1], decodeURIComponent(m[2])));
2098
+ render(m[1], decodeURIComponent(m[2]));
2082
2099
  }
2083
2100
  }
2084
2101
  let promise = pxt.editor.initEditorExtensionsAsync();
2085
2102
  promise.then(() => {
2086
2103
  window.addEventListener("message", receiveDocMessage, false);
2087
2104
  window.addEventListener("hashchange", () => {
2088
- renderHash();
2105
+ renderHashAsync();
2089
2106
  }, false);
2090
2107
  parent.postMessage({ type: "sidedocready" }, "*");
2091
2108
  // delay load doc page to allow simulator to load first
2092
- setTimeout(() => renderHash(), 1);
2109
+ setTimeout(() => renderHashAsync(), 1);
2093
2110
  });
2094
2111
  }
2095
2112
  runner.startDocsServer = startDocsServer;
@@ -2108,7 +2125,7 @@ var pxt;
2108
2125
  if (files[readme])
2109
2126
  md += files[readme].replace(/^#+/, "$0#") + '\n'; // bump all headers down 1
2110
2127
  cfg.files.filter(f => f != pxt.CONFIG_NAME && f != readme)
2111
- .filter(f => (runner.editorLanguageMode == LanguageMode.Blocks) == /\.blocks?$/.test(f))
2128
+ .filter(f => matchesLanguageMode(f, runner.editorLanguageMode))
2112
2129
  .forEach(f => {
2113
2130
  if (!/^main\.(ts|blocks)$/.test(f))
2114
2131
  md += `
@@ -2163,6 +2180,16 @@ ${linkString}
2163
2180
  return renderMarkdownAsync(content, md, options);
2164
2181
  }
2165
2182
  runner.renderProjectFilesAsync = renderProjectFilesAsync;
2183
+ function matchesLanguageMode(filename, mode) {
2184
+ switch (mode) {
2185
+ case LanguageMode.Blocks:
2186
+ return /\.blocks?$/.test(filename);
2187
+ case LanguageMode.TypeScript:
2188
+ return /\.ts?$/.test(filename);
2189
+ case LanguageMode.Python:
2190
+ return /\.py?$/.test(filename);
2191
+ }
2192
+ }
2166
2193
  function renderDocAsync(content, docid) {
2167
2194
  docid = docid.replace(/^\//, "");
2168
2195
  return pxt.Cloud.markdownAsync(docid)