How to pass parameters to THttpServer using python?

I am using root.js and querying a TH2I via JavaScript.
According to the THttpServer documentation am doing in Python:

import ROOT


def do_something():
    print("do something")


class Th2Maker():
    def do_something(self):
       print("do_something_member")

    def __init__(self):
       self.__serv = ROOT.THttpServer("http:8080")
       self.__serv.CreateServerThread()
       self.__histo = ROOT.TH2I('h1', '', 
                                self.__X_BIN_MAX, 0, self.__X_BIN_MAX,
                                self.__Y_BIN_MAX, 0, self.__Y_BIN_MAX)
       self.__histo.SetName("histo")
       for x in range(0, self.__X_BIN_MAX):
           for y in range(0, self.__Y_BIN_MAX):
               self.__histo.SetBinContent(x, y, x+y)
       self.__serv.Register("subfolder", self.__histo)
       self.__serv.RegisterCommand('/Folder/Start', 'do_something()')
       self.__serv.RegisterCommand('/Folder/Start2', 'self.do_something()')

    __X_BIN_MAX: Final = 200
    __Y_BIN_MAX: Final = 200 

Calling wget .../Folder/Start(2)/cmd.json -O result.txt results in

use of undeclared identifier 'do_something'

What is the Python version of passing functions to RegisterCommand()?

Welcome to the ROOT Forum!
I might be wrong, but I think do_something() must be known by the interpreter, and then declared in it. Something like:

   gInterpreter->Declare("void do_something();");

or even

   gInterpreter->Declare("void do_something() { printf(\"do something\"); }");

But maybe @etejedor knows a better way

Thanks a lot for the very fast replay.

That might be the C++ way. But I need Python.

ROOT.gInterpreter.Declare("do_something()")

does not work there.

ROOT.gInterpreter.Declare("void do_something(){std::cout << \"jjjjj\" << std::endl;}");

works - but I need to access the rest of my Python code in the function.

So @etejedor, our PyROOT expert can help you with that

If do_something is a free function in Python, you can do:

self.__serv.RegisterCommand('/Folder/Start', 'TPython::Exec("do_something()")')

self.do_something won’t work because TPython will run the code you pass in the global scope of Python. What you could do is create a o = Th2Maker() object in the global scope and do o.do_something() in TPython.

Basic version (without parameters and class):

import signal
import threading

import ROOT


def do_something():
     print("do something")


serv = ROOT.THttpServer("http:8080")
serv.CreateServerThread()
serv.RegisterCommand('/Folder/Start', 'TPython::Exec("do_something()")')


def keep_alive():
     pass


x = threading.Thread(target=keep_alive)
x.start()
signal.pause()

wget http://localhost:8080/Folder/Start/cmd.json -O result.txt → result:

Info in <THttpEngine::Create>: Starting HTTP server on port 8080
 *** Break *** segmentation violation
...
The lines below might hint at the cause of the crash.
You may get help by asking at the ROOT forum https://root.cern.ch/forum
Only if you are really convinced it is a bug in ROOT then please submit a
report at https://root.cern.ch/bugs Please post the ENTIRE stack trace
from above as an attachment in addition to anything else
that might help us fixing this issue.
===========================================================
#7  0x000000000055165c in PyImport_GetModuleDict ()
#8  0x0000000000683c9e in PyImport_AddModule ()
#9  0x00007f4e0855f1bc in TPython::Initialize() () from /home/jens/bin/root/lib/libROOTTPython.so.6.22.08
#10 0x00007f4e0855f813 in TPython::Exec(char const*) () from /home/jens/bin/root/lib/libROOTTPython.so.6.22.08
#11 0x00007f4e1273103e in ?? ()
#12 0x00007f4dfcff80a0 in ?? ()
#13 0x000000000379dce0 in ?? ()
#14 0x000000000379dce0 in ?? ()
#15 0x00007f4dfcff8490 in ?? ()
#16 0x0000000002059c90 in ?? ()
#17 0x00007f4dfcff8490 in ?? ()
#18 0x0000000003952a30 in ?? ()
#19 0x00007f4e1677e924 in cling::IncrementalExecutor::executeWrapper(llvm::StringRef, cling::Value*) const () from /home/jens/bin/root/lib/libCling.so
#20 0x00007f4e16706767 in cling::Interpreter::RunFunction(clang::FunctionDecl const*, cling::Value*) () from /home/jens/bin/root/lib/libCling.so
#21 0x00007f4e1670806d in cling::Interpreter::EvaluateInternal(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, cling::CompilationOptions, cling::Value*, cling::Transaction**, unsigned long) () from /home/jens/bin/root/lib/libCling.so
#22 0x00007f4e16708389 in cling::Interpreter::process(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, cling::Value*, cling::Transaction**, bool) () from /home/jens/bin/root/lib/libCling.so
#23 0x00007f4e167d151d in cling::MetaProcessor::process(llvm::StringRef, cling::Interpreter::CompilationResult&, cling::Value*, bool) () from /home/jens/bin/root/lib/libCling.so
#24 0x00007f4e1665c39c in HandleInterpreterException(cling::MetaProcessor*, char const*, cling::Interpreter::CompilationResult&, cling::Value*) () from /home/jens/bin/root/lib/libCling.so
===========================================================
...

Another option could be that RegisterCommand implements an overload that accepts an std::function, since those are directly convertible from a Python callable, but I think that’s not the case now @bellenot?

On the other hand, the example works if you don’t spawn a server thread:

import signal
import threading

import ROOT

def do_something():
     print("do something")


serv = ROOT.THttpServer("http:8080")
#serv.CreateServerThread()
serv.RegisterCommand('/Folder/Start', 'TPython::Exec("do_something()")')


def keep_alive():
     pass


x = threading.Thread(target=keep_alive)
x.start()
signal.pause()

Thanks a lot - It seems that I forgot to remove it or never test without it after inserting the threading / signal to run the program forever without blocking the THttpServer.

P.S.:
One last question: What is about passing strings as parameters? %argN% seems to be an fixed topic for RegisterCommand (and it is not for http / GET). Passing integers or floats works but when passing strings I got errors:

...
 def do_something(arg1=None):
      print("do something", arg1)

...
serv.RegisterCommand('/Folder/Start', 'TPython::Exec("do_something(%arg1%)")')
wget http://localhost:8080/Folder/Start/cmd.json?arg1=rr -O result.txt
Traceback (most recent call last):
  File "<string>", line 1, in <module>
NameError: name 'rr' is not defined

The intent is to pass a filename for data input for TH2I.

No, it’s not the case…

Hello,

I think you need to do (notice the quotes around %arg1):

serv.RegisterCommand('/Folder/Start', 'TPython::Exec("do_something(\"%arg1%\")")')

So you need to make sure the function receives a string literal "rr", otherwise the arg is treated as a variable name rr (this applies both for Python and C++).

OK - in Python single quotes are needed:

serv.RegisterCommand('/Folder/Start', 'TPython::Exec("do_something(\'%arg1%\')")')

Then passing wget http://localhost:8080/Folder/Start/cmd.json?arg1=rr -O result.txt works as expected.

Thanks a lot - for my part the issue can be closed.

It is different when running inside a docker container. Without serv.CreateServerThread() the process is halted and not processing any requests. Unfortunality with self.__serv.CreateServerThread() it crashes when receiving the request set via RegisterCommand while a request for a histogram (set via self.__serv.Register("subfolder", self.__histo) works.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.