Using a device mnemonic routine and redirecting IO also seems to work. I used this very basic routine to log to IO:

ZJES01	;
rchr(c)      quit
	#;Read a string - we don't care about reading
rstr(sz,to)  quit
	#;Write a character - call the output label
wchr(s)      do output($char(s))  quit
	#;Write a form feed - call the output label
wff()        do output($char(12))  quit
	#;Write a newline - call the output label
wnl()        do output($char(13,10))  quit
	#;Write a string - call the output label
wstr(s)      do output(s)  quit
	#;Write a tab - call the output label
wtab(s)      do output($char(9))  quit
	#;Output label - this is where you would handle what you actually want to do.
	#;  in our case, we want to write to str
output(s) set ^ZJES($increment(^ZJES))=s quit

And then used the following classmethod to test:

ClassMethod TestRedirect()
{
	
	set MnemonicRoutine = ##class(%Device).GetMnemonicRoutine()
	use $io::("^ZJES01")
	set RedirectIO=##class(%Device).ReDirectIO(1)
	do ..PythonPrint()
	do ##class(%Device).ReDirectIO(RedirectIO)
	use $io::("^"_MnemonicRoutine)
	quit
}

Which gave me:

zwrite ^ZJES
^ZJES=3
^ZJES(1)="Python print this line to the buffer"
^ZJES(2)=$c(13,10)
^ZJES(3)="IRIS write this line to the buffer"

How is the buffer class capturing the output? I did a very quick testing using the spool device and a USE statement and the output appeared in ^SPOOL as expected:

/// Open spool device and call Embedded Python method.
ClassMethod TestSpool()
{
	set SpoolFile = $order(^SPOOL(""),-1)+1
	open 2:SpoolFile
	use 2
	do ..PythonPrint()
	close 2
	break
}

ClassMethod PythonPrint() [ Language = python ]
{
	import iris 
	print("Python print this line to the buffer")
	iris.execute('write "IRIS write this line to the buffer"')
}
zwrite SpoolFile
SpoolFile=956

zwrite ^SPOOL(956)
^SPOOL(956,1)="Python print this line to the buffer"_$c(10)
^SPOOL(956,2)="IRIS write this line to the buffer"
^SPOOL(956,2147483647)="{67424,57942{3{"

You can use the SQL DATEDIFF function in ObjectScript to get the number of minutes and divide by 60:

write $system.SQL.Functions.DATEDIFF("mi","2024-07-12 08:30:00","2024-07-12 15:15:00")/60  
6.75

Documentation is here: https://docs.intersystems.com/iris20252/csp/documatic/%25CSP.Documatic.c...

You can pass "hh" in as the first argument to get hours, but only returns whole hours rounded up. If you need second accuracy, you can pass in "ss" for "seconds" and divide by 3600 instead.

There's a method on the iris.gref class called "data".

set ^zJES(1)="$data = 1"
set ^zJES(2,0)="$data = 10"
set ^zJES(4) = ""
set ^zJES(4,0) = ""

The result ends up matching $data:

>>> glb = iris.gref("^zJES") 
>>> print(glb.data())
10
>>> print(glb.data([1])) 
1
>>> print(glb.data([2]))
10
>>> print(glb.data([3]))
0
>>> print(glb.data([4]))
11

I found running help(iris) at the Python shell helpful for working this kind of stuff out.

Edit: Unlike @Robert.Cemper1003's solution this does not return the value of the node we're testing (like the 2 parameter usage $data)

It's probably worth mentioning that this was always the limit. Prior to IRIS 2020.1 (from memory) any characters after the first 31 were just ignored, so in the above example the global would be truncated to ^Jobcosting.JobActivityGroupGrou. There is now a hard stop in the class compiler that prevents global names longer than the limit as there was the potential for truncated global names to clash.

You can also try overriding the GUIDENABLED parameter from %Persistent:

Class User.Test1 Extends %Persistent

{

Parameter GUIDENABLED = 1;

Property Property1 as %String;

Storage Default

{

<Data name="Test1DefaultData">

<Value name="1">

<Value>%%CLASSNAME</Value>

</Value>

<Value name="2">

<Value>Property1</Value>

</Value>

</Data>

<DataLocation>^User.Test1D</DataLocation>

<DefaultData>Test1DefaultData</DefaultData>

<IdLocation>^User.Test1D</IdLocation>

<IndexLocation>^User.Test1I</IndexLocation>

<StreamLocation>^User.Test1S</StreamLocation>

<Type>%Library.CacheStorage</Type>

}


}

Will give you:

USER>set obj = ##class(Test1).%New()

USER>set obj.Property1 = "Hello"

USER>w obj.%Save()

1

USER>w obj."%%GUID"

FC381F94-277E-11E8-82F0-005056B479AA

The ^OBJ.GUID index that this creates can be accessed via %ExtentMgr.GUID.