From 2acdba23f91c05d7a83a9f733143ea21cc2bf51e Mon Sep 17 00:00:00 2001 From: Doug Torrance Date: Sun, 24 May 2026 14:14:13 -0400 Subject: [PATCH 1/4] Add "with ... do" statement for context management We add support for 4 types: * File (close on exit) * Mutex (lock on entrance, unlock on exit) * EngineRing (use on entrance, restore previous symbol values on exit) * InexactField (sets and restores defaultPrecision) --- M2/Macaulay2/d/binding.d | 11 ++++++++++ M2/Macaulay2/d/common.d | 1 + M2/Macaulay2/d/convertr.d | 1 + M2/Macaulay2/d/debugging.dd | 7 ++++++ M2/Macaulay2/d/evaluate.d | 16 ++++++++++++++ M2/Macaulay2/d/parse.d | 7 ++++-- M2/Macaulay2/d/parser.d | 12 +++++++++++ M2/Macaulay2/m2/enginering.m2 | 6 ++++++ M2/Macaulay2/m2/exports.m2 | 3 +++ M2/Macaulay2/m2/files.m2 | 2 ++ M2/Macaulay2/m2/methods.m2 | 3 +++ M2/Macaulay2/m2/reals.m2 | 5 +++++ M2/Macaulay2/m2/threads.m2 | 3 +++ M2/Macaulay2/tests/normal/with.m2 | 36 +++++++++++++++++++++++++++++++ 14 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 M2/Macaulay2/tests/normal/with.m2 diff --git a/M2/Macaulay2/d/binding.d b/M2/Macaulay2/d/binding.d index ae8020f6885..11294c1ad6f 100644 --- a/M2/Macaulay2/d/binding.d +++ b/M2/Macaulay2/d/binding.d @@ -333,6 +333,7 @@ bumpPrecedence(); special("if", unaryif, precSpace, wide); special("try", unarytry, precSpace, wide); special("catch", unarycatch, precSpace, wide); + special("with", unarywith, precSpace, wide); bumpPrecedence(); export ParenStarParenS := makeKeyword(postfix("(*)")); bumpPrecedence(); @@ -411,6 +412,12 @@ export RobustPrintNetE := Expr(RobustPrintNetS); export RobustPrintStringS := makeProtectedSymbolClosure("RobustPrintStringMethod"); export RobustPrintStringE := Expr(RobustPrintStringS); +export EnterMethodS := makeProtectedSymbolClosure("EnterMethod"); +export EnterMethodE := Expr(EnterMethodS); + +export ExitMethodS := makeProtectedSymbolClosure("ExitMethod"); +export ExitMethodE := Expr(ExitMethodS); + export StopIterationS := makeProtectedSymbolClosure("StopIteration"); export StopIterationE := Expr(StopIterationS); @@ -946,6 +953,10 @@ export bind(e:ParseTree,dictionary:Dictionary):void := ( is i:Catch do ( bind(i.primary,dictionary); ) + is i:WithDo do ( + bind(i.primary,dictionary); + bind(i.doClause,dictionary); + ) ); export localBind(e:ParseTree,dictionary:Dictionary):bool := ( HadError = false; diff --git a/M2/Macaulay2/d/common.d b/M2/Macaulay2/d/common.d index bab7f50ca02..ba3ed04e867 100644 --- a/M2/Macaulay2/d/common.d +++ b/M2/Macaulay2/d/common.d @@ -42,6 +42,7 @@ export codePosition(c:Code):Position := ( -- TODO retire is f:adjacentCode do f.position is f:functionCode do f.position is f:catchCode do f.position + is f:withDoCode do f.position is f:Error do f.position ); diff --git a/M2/Macaulay2/d/convertr.d b/M2/Macaulay2/d/convertr.d index 9c49aff06b7..a5a419499a9 100644 --- a/M2/Macaulay2/d/convertr.d +++ b/M2/Macaulay2/d/convertr.d @@ -321,6 +321,7 @@ export convert0(e:ParseTree):Code := ( else if n.newInitializer == dummyTree then Code(newOfCode( convert(n.newClass), convert(n.newParent), pos)) else Code(newOfFromCode(convert(n.newClass), convert(n.newParent), convert(n.newInitializer), pos))) + is w:WithDo do Code(withDoCode(convert(w.primary), convert(w.doClause), pos)) is d:dummy do dummyCode ); diff --git a/M2/Macaulay2/d/debugging.dd b/M2/Macaulay2/d/debugging.dd index 48c44b615b2..a095e14885b 100644 --- a/M2/Macaulay2/d/debugging.dd +++ b/M2/Macaulay2/d/debugging.dd @@ -86,6 +86,7 @@ export tostring(c:Code):string := ( is x:newFromCode do concatenate(array(string)("(new ", tostring(x.newClause), " from: ", tostring(x.fromClause), ")")) is x:newOfCode do concatenate(array(string)("(new ", tostring(x.newClause), " of: ", tostring(x.ofClause), ")")) is x:newCode do concatenate(array(string)("(new ", tostring(x.newClause), ")")) + is x:withDoCode do concatenate(array(string)("(with ", tostring(x.primary), " do: ", tostring(x.doClause), ")")) ); export toExpr(c:Code):Expr := Pseudocode(c); @@ -166,6 +167,8 @@ export toList(c:Code):Expr := ( is x:newOfCode do list(toExpr("new"), seq(toExpr(x.newClause), list(toExpr("of"), toExpr(x.ofClause)))) is x:newCode do list(toExpr("new"), seq(toExpr(x.newClause))) -- + is x:withDoCode do list(toExpr("with"), seq(toExpr(x.primary), list(toExpr("do"), toExpr(x.doClause)))) + -- else toExpr(tostring(c))); disassemble(e:Expr):Expr := ( @@ -426,4 +429,8 @@ export toExpr(e:ParseTree):Expr := ( toExpr(n.newClass), toExpr(n.newParent), toExpr(n.newInitializer)) + is w:WithDo do list( + toExpr("WithDo"), + toExpr(w.primary), + toExpr(w.doClause)) is dummy do list(toExpr("dummy"))); diff --git a/M2/Macaulay2/d/evaluate.d b/M2/Macaulay2/d/evaluate.d index e1f5305a8ef..f38e6511a69 100644 --- a/M2/Macaulay2/d/evaluate.d +++ b/M2/Macaulay2/d/evaluate.d @@ -1298,6 +1298,21 @@ parallelAssignmentFun(x:parallelAssignmentCode):Expr := ( else ParallelAssignmentError(nlhs)) else ParallelAssignmentError(nlhs)); +evalWithDoCode(c:withDoCode):Expr := ( + x := eval(c.primary); + when x is Error do return x else nothing; + entermethod := lookup(Class(x), EnterMethodE); + if entermethod == nullE + then return buildErrorPacket("no enter method found"); + exitmethod := lookup(Class(x), ExitMethodE); + if exitmethod == nullE + then return buildErrorPacket("no exit method found"); + before := applyEE(entermethod, x); + when before is Error do return before else nothing; + r := eval(c.doClause); + applyEE(exitmethod, x); + r); + -- helper function used when evaluating tryCode and by null coalescion -- tryCaughtError is false unless an (non-interrupting) error occurred threadLocal tryCaughtError := false; @@ -1738,6 +1753,7 @@ export evalraw(c:Code):Expr := ( is c:newOfCode do NewOfFun(c.newClause,c.ofClause) is c:newFromCode do NewFromFun(c.newClause,c.fromClause) is c:newOfFromCode do NewOfFromFun(c.newClause,c.ofClause,c.fromClause) + is c:withDoCode do evalWithDoCode(c) is nullCode do return nullE is v:realCode do return Expr(RRcell(v.x)) is v:integerCode do return Expr(ZZcell(v.x)) diff --git a/M2/Macaulay2/d/parse.d b/M2/Macaulay2/d/parse.d index 6322ba40b46..0c73c589f55 100644 --- a/M2/Macaulay2/d/parse.d +++ b/M2/Macaulay2/d/parse.d @@ -145,6 +145,7 @@ export Unary := {+Operator:Token, rhs:ParseTree}; export Postfix:= {+lhs:ParseTree, Operator:Token}; export Parentheses := {+ left:Token, contents:ParseTree, right:Token }; export EmptyParentheses := {+ left:Token, right:Token }; +export WithDo := {+ withToken:Token, primary:ParseTree, doClause:ParseTree}; export dummy := {+position:Position}; export ParseTree := ( @@ -153,7 +154,7 @@ export ParseTree := ( or Unary or Binary or Postfix or IfThen or IfThenElse or Try or TryThen or TryThenElse or TryElse or TryThenDo or TryDo or Catch or WhileDo or WhileListDo or WhileList or For - or New + or New or WithDo or dummy ); -- Code @@ -242,6 +243,8 @@ export newFromCode := {+newClause:Code,fromClause:Code,position:Position}; export newOfCode := {+newClause:Code,ofClause:Code,position:Position}; export newCode := {+newClause:Code,position:Position}; +export withDoCode := {+primary:Code,doClause:Code,position:Position}; + export CodeSequence := tarray(Code); export sequenceCode := {+x:CodeSequence, position:Position}; export listCode := {+y:CodeSequence, position:Position}; @@ -274,7 +277,7 @@ export Code := ( or unaryCode or binaryCode or ternaryCode or multaryCode or forCode or sequenceCode or listCode or arrayCode or angleBarListCode or semiCode or newCode or newFromCode or newOfCode or newOfFromCode - or whileDoCode or whileListCode or whileListDoCode + or whileDoCode or whileListCode or whileListDoCode or withDoCode or ifCode or tryCode or adjacentCode or functionCode or catchCode or Error -- for tail recursion ); diff --git a/M2/Macaulay2/d/parser.d b/M2/Macaulay2/d/parser.d index 957b61bf82b..74f0e29430b 100644 --- a/M2/Macaulay2/d/parser.d +++ b/M2/Macaulay2/d/parser.d @@ -522,6 +522,16 @@ export unarynew(newtoken:Token,file:TokenFile,prec:int,obeylines:bool):ParseTree if newinitializer == errorTree then return errorTree; ); accumulate(ParseTree(New(newtoken,newclass,newparent,newinitializer)),file,prec,obeylines)); +export unarywith(withToken:Token,file:TokenFile,prec:int, obeylines:bool):ParseTree := ( + primary := parse(file,withToken.word.parse.unaryStrength,obeylines); + if primary == errorTree then return errorTree; + doToken := gettoken(file,false); + if doToken.word == doW then ( + doClause := parse(file,doW.parse.unaryStrength,obeylines); + if doClause == errorTree then errorTree + else accumulate(ParseTree(WithDo(withToken,primary,doClause)),file,prec,obeylines)) + else if doToken == errorToken then errorTree + else makeParseError(doToken,"syntax error: expected 'do'")); export treePosition(e:ParseTree):Position := ( when e @@ -559,6 +569,7 @@ export treePosition(e:ParseTree):Position := ( if t.newInitializer != dummyTree then t.newInitializer else if t.newParent != dummyTree then t.newParent else t.newClass; combinePositionL(t.newToken.position, treePosition(lastClass))) + is t:WithDo do combinePositionL(t.withToken.position, treePosition(t.doClause)) is t:dummy do t.position ); @@ -594,6 +605,7 @@ export size(e:ParseTree):int := ( is x:WhileList do Ccode(int,"sizeof(*",x,")") + size(x.whileToken) + size(x.predicate) + size(x.listClause) is x:WhileListDo do Ccode(int,"sizeof(*",x,")") + size(x.whileToken) + size(x.predicate) + size(x.doClause) + size(x.listClause) is x:New do Ccode(int,"sizeof(*",x,")") + size(x.newToken) + size(x.newClass) + size(x.newParent) + size(x.newInitializer) + is x:WithDo do Ccode(int,"sizeof(*",x,")") + size(x.withToken) + size(x.primary) + size(x.doClause) ); -- Local Variables: diff --git a/M2/Macaulay2/m2/enginering.m2 b/M2/Macaulay2/m2/enginering.m2 index 3a1ef19a813..abbb21760af 100644 --- a/M2/Macaulay2/m2/enginering.m2 +++ b/M2/Macaulay2/m2/enginering.m2 @@ -723,6 +723,12 @@ fraction(RingElement,RingElement) := (r,s) -> ( Ring _ String := RingElement => (x,s) -> x.indexStrings#s Ring _ Symbol := RingElement => (x,s) -> x.indexSymbols#s +EngineRing.EnterMethod = R -> ( + scan(R.generatorSymbols, sym -> pushvar(sym, value sym)); + use R) + +EngineRing.ExitMethod = R -> scan(R.generatorSymbols, popvar) + -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/m2 " -- End: diff --git a/M2/Macaulay2/m2/exports.m2 b/M2/Macaulay2/m2/exports.m2 index c3487bb525f..8c5e093dc86 100644 --- a/M2/Macaulay2/m2/exports.m2 +++ b/M2/Macaulay2/m2/exports.m2 @@ -158,11 +158,13 @@ export { "End", "Engine", "EngineRing", + "EnterMethod", "Error", "Equation", "EulerConstant", "ExampleFiles", "Exclude", + "ExitMethod", "Expression", "Ext", "Fano", @@ -1279,6 +1281,7 @@ export { "when", "while", "width", + "with", "wrap", "xor", "youngest", diff --git a/M2/Macaulay2/m2/files.m2 b/M2/Macaulay2/m2/files.m2 index b4076c0373d..772d4f2b6b8 100644 --- a/M2/Macaulay2/m2/files.m2 +++ b/M2/Macaulay2/m2/files.m2 @@ -527,6 +527,8 @@ scanLines(Function,String) := (p,inf) -> ( -- the function p can use "break ret) scanLines(Function,List) := (p,infs) -> scan(infs,inf->scanLines(p,inf)) +File.ExitMethod = close#0 + -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/m2 " -- End: diff --git a/M2/Macaulay2/m2/methods.m2 b/M2/Macaulay2/m2/methods.m2 index d6a68ca9d37..98b06fbd3b4 100644 --- a/M2/Macaulay2/m2/methods.m2 +++ b/M2/Macaulay2/m2/methods.m2 @@ -725,6 +725,9 @@ registerFinalizer' = registerFinalizer registerFinalizer = method() registerFinalizer(Thing, String) := registerFinalizer' +-- context management +Thing.EnterMethod = Thing.ExitMethod = x -> null + -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/m2 " -- End: diff --git a/M2/Macaulay2/m2/reals.m2 b/M2/Macaulay2/m2/reals.m2 index 1099f10b429..501730b6c84 100644 --- a/M2/Macaulay2/m2/reals.m2 +++ b/M2/Macaulay2/m2/reals.m2 @@ -589,6 +589,11 @@ ring ComplexField := R -> CC ring RealField := R -> RR ring RealIntervalField := R -> RRi +InexactField.EnterMethod = kk -> ( + pushvar(symbol defaultPrecision, defaultPrecision); + defaultPrecision = precision kk) +InexactField.ExitMethod = kk -> popvar symbol defaultPrecision + -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/m2 " -- End: diff --git a/M2/Macaulay2/m2/threads.m2 b/M2/Macaulay2/m2/threads.m2 index 2af6a95ca96..fae950b9300 100644 --- a/M2/Macaulay2/m2/threads.m2 +++ b/M2/Macaulay2/m2/threads.m2 @@ -36,6 +36,9 @@ tryLock Mutex := tryLock0 unlock = method() unlock Mutex := unlock0 +Mutex.EnterMethod = lock +Mutex.ExitMethod = unlock + ----------------------------------------------------------------------------- parallelApplyRaw = (L, f) -> diff --git a/M2/Macaulay2/tests/normal/with.m2 b/M2/Macaulay2/tests/normal/with.m2 new file mode 100644 index 00000000000..7571eb8e387 --- /dev/null +++ b/M2/Macaulay2/tests/normal/with.m2 @@ -0,0 +1,36 @@ +-- context management + +-- files +f = openOut temporaryFileName() +with f do assert isOpen f +assert not isOpen f + +-- mutexes +m = new Mutex +with m do assert try tryLock m then false else true +assert try tryLock m then true else false +unlock m + +-- polynomial rings +R = QQ[x] +x = 5 +assert Equation(with R do x, R_0) +assert Equation(x, 5) + +-- inexact fields +prec = defaultPrecision +assert Equation(with RR_100 do defaultPrecision, 100) +assert Equation(defaultPrecision, prec) + +-- custom types +X = new Type of HashTable +X.EnterMethod = x -> y = 1 +X.ExitMethod = x -> y = 2 +y = 0 +with new X do assert Equation(y, 1) +assert Equation(y, 2) + +-- ensure that exit method still runs even w/ an error +y = 3 +try with new X do 1/0 +assert Equation(y, 2) From db1f7c381776ebd9afc428ad9a8bf44f8f6ebd50 Mon Sep 17 00:00:00 2001 From: Doug Torrance Date: Sun, 24 May 2026 21:24:38 -0400 Subject: [PATCH 2/4] Document with ... do statements --- .../packages/Macaulay2Doc/doc_mutex.m2 | 1 + .../Macaulay2Doc/functions/use-doc.m2 | 2 +- .../Macaulay2Doc/ov_analytic_functions.m2 | 3 +- .../packages/Macaulay2Doc/ov_language.m2 | 61 +++++++++++++++++++ .../packages/Macaulay2Doc/ov_system.m2 | 3 +- 5 files changed, 67 insertions(+), 3 deletions(-) diff --git a/M2/Macaulay2/packages/Macaulay2Doc/doc_mutex.m2 b/M2/Macaulay2/packages/Macaulay2Doc/doc_mutex.m2 index d9cc1315d0e..e30c346b29e 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/doc_mutex.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/doc_mutex.m2 @@ -42,6 +42,7 @@ doc /// SeeAlso "parallel programming with threads and tasks" AtomicInt + symbol with Subnodes (NewMethod, Mutex) (lock, Mutex) diff --git a/M2/Macaulay2/packages/Macaulay2Doc/functions/use-doc.m2 b/M2/Macaulay2/packages/Macaulay2Doc/functions/use-doc.m2 index 8f024df93ca..fb363f65da2 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/functions/use-doc.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/functions/use-doc.m2 @@ -74,5 +74,5 @@ document { ///, Caveat => {"Any values stored in the variables that have been assigned to are lost, hence this operation should not be used by code in a package."}, - SeeAlso => {GlobalAssignHook} + SeeAlso => {GlobalAssignHook, symbol with} } diff --git a/M2/Macaulay2/packages/Macaulay2Doc/ov_analytic_functions.m2 b/M2/Macaulay2/packages/Macaulay2Doc/ov_analytic_functions.m2 index 3fb8fe12fbb..eb1a0e181c1 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/ov_analytic_functions.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/ov_analytic_functions.m2 @@ -629,7 +629,8 @@ document { Key => "defaultPrecision", 1/3. RR[x] numeric pi - /// + ///, + SeeAlso => {symbol with}, } undocumented { diff --git a/M2/Macaulay2/packages/Macaulay2Doc/ov_language.m2 b/M2/Macaulay2/packages/Macaulay2Doc/ov_language.m2 index 1d7f4d8ff08..c5a754e236b 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/ov_language.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/ov_language.m2 @@ -548,6 +548,7 @@ document { TO "break", TO "continue", TO "return", + TO "with", } } @@ -1266,6 +1267,66 @@ document { Subnodes => TO seeParsing } +doc /// + Key + symbol with + symbol EnterMethod + symbol ExitMethod + Headline + context management + Usage + with x do y + Inputs + x:Thing + y: -- Macaulay2 code + Description + Text + First, @VAR "x"@ is evaluated. The @M2CODE "EnterMethod"@ for the + type of @VAR "x"@ is run with @VAR "x"@ as input. Then @VAR "y"@ is + evaluated, and finally the @M2CODE "ExitMethod"@ for the type of + @VAR "x"@ is run with @VAR "x"@ as input, even if evaluating @VAR "y"@ + failed. + + For most Macaulay2 types, the enter and exit methods do nothing. But + there are several useful cases. + + If @VAR "x"@ is @ofClass File@, then @TO close@ is called on exit. + Example + file = openOut temporaryFileName(); + try with file do (file << "Hello, world!" << endl; 1/0) + isOpen file + Text + If @VAR "x"@ is @ofClass Mutex@ then @TO lock@ is called on entrance and + @TO unlock@ on exit. + Example + mutex = new Mutex + with mutex do last trap tryLock mutex + tryLock mutex + unlock mutex + Text + If @VAR "x"@ is @ofClass EngineRing@, then @TO (use, Ring)@ is called + on entrance, but the previous values of the variables of the ring are + restored on exit. + Example + R = QQ[x] + x = 5 + with R do x^2 + 3 + x + Text + If @VAR "x"@ is @ofClass InexactField@, then @TO "defaultPrecision"@ is + set to the precision of the given field on entrance and restored on exit. + Example + with RR_100 do numeric pi + numeric pi + Text + It is possible to define enter and exit methods for any type. + Example + X = new Type of HashTable; + X.EnterMethod = x -> print "hi!"; + X.ExitMethod = x -> print "bye!"; + with new X do print "in the do clause!" +/// + -- Local Variables: -- compile-command: "make -C $M2BUILDDIR/Macaulay2/m2 " -- End: diff --git a/M2/Macaulay2/packages/Macaulay2Doc/ov_system.m2 b/M2/Macaulay2/packages/Macaulay2Doc/ov_system.m2 index 2d31720a12b..9c389dbc4af 100644 --- a/M2/Macaulay2/packages/Macaulay2Doc/ov_system.m2 +++ b/M2/Macaulay2/packages/Macaulay2Doc/ov_system.m2 @@ -1478,7 +1478,8 @@ document { it has received all its input.", PARA{}, "If the file is ", TT "stdio", " then it is left open, and - no error is signaled." + no error is signaled.", + SeeAlso => {symbol with}, } document { From 0dcc22b6315556a22b04787eb08dc49f9eadd0ce Mon Sep 17 00:00:00 2001 From: Doug Torrance Date: Sun, 24 May 2026 21:27:03 -0400 Subject: [PATCH 3/4] Use a mutex (w/ new "with" syntax) in makeMonomialOrdering It modifies several global variables and was not thread-safe. --- M2/Macaulay2/m2/engine.m2 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/M2/Macaulay2/m2/engine.m2 b/M2/Macaulay2/m2/engine.m2 index 859ee169c95..312beecf549 100644 --- a/M2/Macaulay2/m2/engine.m2 +++ b/M2/Macaulay2/m2/engine.m2 @@ -187,7 +187,9 @@ processWeights = (nvars,weights) -> ( then error("Weights: expected weight vector of length ",toString nvars," but got ",toString (#wt)))); weights); +makeMonomialOrderingMutex := new Mutex makeMonomialOrdering = (monsize,inverses,nvars,degs,weights,ordering) -> ( + with makeMonomialOrderingMutex do ( -- 'monsize' is the old MonomialSize option, usually 8 or 16, or 'null' if unset -- 'inverses' is true or false, and tells whether the old "Inverses => true" option was used. -- 'nvars' tells the total number of variables. Any extra variables will be ordered with GRevLex or GroupLex. @@ -214,7 +216,7 @@ makeMonomialOrdering = (monsize,inverses,nvars,degs,weights,ordering) -> ( varcount = 0; t := toList nonnull fixup2 t'; logmo := new FunctionApplication from {rawMonomialOrdering,t}; - (t,t',value logmo, logmo)) + (t,t',value logmo, logmo))) RawMonomialOrdering ** RawMonomialOrdering := RawMonomialOrdering => rawProductMonomialOrdering From e023873f80106298aa406de932e50464d7c04827 Mon Sep 17 00:00:00 2001 From: Doug Torrance Date: Sun, 24 May 2026 21:55:12 -0400 Subject: [PATCH 4/4] Add support for context management in Python package Also bump Python package to version 1.1 --- M2/Macaulay2/packages/Python.m2 | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/M2/Macaulay2/packages/Python.m2 b/M2/Macaulay2/packages/Python.m2 index cf51720c27e..054d949737b 100644 --- a/M2/Macaulay2/packages/Python.m2 +++ b/M2/Macaulay2/packages/Python.m2 @@ -1,10 +1,10 @@ --* +-* this does not work unless M2 is compiled --with-python *- newPackage("Python", - Version => "1.0", - Date => "November 8, 2025", + Version => "1.1", + Date => "May 24, 2026", Headline => "interface to Python", Authors => { {Name => "Daniel R. Grayson", @@ -27,6 +27,15 @@ newPackage("Python", -* +1.1 (2026-05-24, M2 1.26.06) +* add support for using with ... do ... statements w/ python objects +* update my contact info +* update to use ~ as a prefix unary operator instead of postfix +* switch to using cached rather than canned example for matplotlib tutorial + (contribution by Paul Zinn-Justin) +* improved webapp support, in particular for displaying matploblib graphics + in the webapp REPL (contribution by Paul Zinn-Justin) + 1.0 (2025-11-08, M2 1.25.11) * New PythonContext class replacing undocumented Context class * getattr, hasattr, and settattr removed (use @@ and @@? now) @@ -481,6 +490,9 @@ if math@@?remainder then ( help#0 PythonObject := x -> toString x@@"__doc__" +PythonObject.EnterMethod = x -> x@@"__enter__"() +PythonObject.ExitMethod = x -> x@@"__exit__"() + ------------------- -- PythonContext -- ------------------- @@ -1012,4 +1024,11 @@ assert Equation(gcd toPython 200, 200) assert Equation(lcm(toPython 200, toPython 300), 600) /// +TEST /// +-- context management +open = pythonValue "open" +with f = open(temporaryFileName(), "w") do () +assert value f@@"closed" +/// + end --------------------------------------------------------