mmtinterface.py 9.95 KB
Newer Older
Theresa Pollinger's avatar
Theresa Pollinger committed
1
2
# to run mmt server : cf. https://docs.python.org/3/library/subprocess.html
import subprocess
3
# subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE) # TODO
4
import time
5
# for now, running
Theresa Pollinger's avatar
Theresa Pollinger committed
6
7
8
# mmt
# extension info.kwarc.mmt.interviews.InterviewServer
# server on 8080
9
import _thread
10
# TODO ask dennis on whether and how to delete modules
Theresa Pollinger's avatar
Theresa Pollinger committed
11
12

# to do http requests
13
# http://docs.python-requests.org/en/master/user/quickstart/
Theresa Pollinger's avatar
Theresa Pollinger committed
14
15
import requests
from requests.utils import quote
Theresa Pollinger's avatar
Theresa Pollinger committed
16
#from urllib.parse import urlencode # is what we actually want to use
Theresa Pollinger's avatar
Theresa Pollinger committed
17
from lxml import etree
Theresa Pollinger's avatar
Theresa Pollinger committed
18
#from openmath import openmath
Theresa Pollinger's avatar
Theresa Pollinger committed
19

20

21
def run_mmt_server():
22
    #subprocess.run(["/home/freifrau/Desktop/masterarbeit/mmt/deploy/mmt.jar", "file", "server-interview.msl"])
23
    # TODO keep alive - or wait for jupyter kernel
24
25
26
27
28
29
30
31
32
    process = subprocess.Popen(["/usr/bin/java", "-jar", "/home/freifrau/Desktop/masterarbeit/mmt/deploy/mmt.jar"], stdin=subprocess.PIPE, universal_newlines=True)# stdout=subprocess.PIPE)
    time.sleep(1000)
    process.stdin.write('server on 8080')
    process.stdin.flush()
    while True:
        time.sleep(100)
    process.stdin.close()
    #print('Waiting for mmt to exit')
    process.wait()
33

34

Theresa Pollinger's avatar
Theresa Pollinger committed
35
class MMTServerError(Exception):
36
    def __init__(self, err, longerr=None):
Theresa Pollinger's avatar
Theresa Pollinger committed
37
        self.error = err
38
39
        self.longerr = longerr
        super(MMTServerError, self).__init__("MMT server error: " + str(self.error), longerr)
Theresa Pollinger's avatar
Theresa Pollinger committed
40
41
42


class MMTReply:
43
44
45
    """An object that holds
        ok : whether the request was successful and
        root: the returned answer, usually an etree holding the xml reply"""
46

47
48
49
    def __init__(self, ok, root=None):
        self.ok = ok
        self.root = root
50
51
        #        print("creating reply")
        #        print(root)
52
53
        if isinstance(root, etree._Element):
            for element in root.iter():
54
55
                # for element in root.iter("div"): # why not entering this loop?
                # print("for element " + elementToString(element))
56
57
58
59
                if (element.get('class')) == 'error':
                    self.ok = False
                    for child in element:
                        if (child.get('class')) == 'message':
Theresa Pollinger's avatar
Theresa Pollinger committed
60
                            #print(element_to_string(self.root))
61
                            raise MMTServerError(child.text, element_to_string(self.root))
62
                            return
Theresa Pollinger's avatar
Theresa Pollinger committed
63
        if not self.ok:
64
            raise MMTServerError(element_to_string(self.root))
Theresa Pollinger's avatar
Theresa Pollinger committed
65
66
67

    def getConstant(self, constantname):
        elements = self.getConstants()
68
        # print ("elements: " + str(elements))
Theresa Pollinger's avatar
Theresa Pollinger committed
69
70
71
72
73
74
75
        for element in elements:
            if element.get('name') == constantname:
                return element

    def getConstants(self):
        elements = []
        for element in self.root.iter("constant"):
76
            # print("Constant: %s - %s - %s" % (element, element.text, element.keys()))
Theresa Pollinger's avatar
Theresa Pollinger committed
77
78
79
            elements.append(element)
        return elements

80
81
    def hasDefinition(self, constantname):
        if self.getDefinition(constantname) is not None:
82
83
            return True

Theresa Pollinger's avatar
Theresa Pollinger committed
84
85
86
87
88
    def getDefinition(self, constantname):
        element = self.getConstant(constantname)
        if element is not None:
            for child in element:
                if (child.tag) == 'definition':
89
                    # print(elementToString(child))
Theresa Pollinger's avatar
Theresa Pollinger committed
90
91
92
93
94
95
96
                    return child

    def getType(self, constantname):
        element = self.getConstant(constantname)
        if element is not None:
            for child in element:
                if (child.tag) == 'type':
97
98
99
100
101
102
103
104
105
106
107
108
109
110
                    print(element_to_string(child))
                    #return child
                    for oms in child.iter("{*}OMS"):
                        return self.get_name_or_expand_if_arrow(oms)

    def get_name_or_expand_if_arrow(self, oms):
        name = oms.get('name')
        if name == 'arrow':
            next = oms.getnext()
            name = next.get('name')
            while next.getnext() is not None:
                next = next.getnext()
                name = name + " → " + next.get('name')
        return name
Theresa Pollinger's avatar
Theresa Pollinger committed
111

112
113
    # (probably very volatile) accesses to concrete data structures
    def getIntervalBoundaries(self, mmtreply, intervalname):
Theresa Pollinger's avatar
Theresa Pollinger committed
114
115
        child = mmtreply.getDefinition(intervalname)
        for oms in child.iter("{*}OMS"):
Theresa Pollinger's avatar
Theresa Pollinger committed
116
117
            #print("OMS: %s - %s - %s" % (oms, oms.text, oms.keys()))
            if (oms.get('name') == 'interval'):
Theresa Pollinger's avatar
Theresa Pollinger committed
118
119
                a = oms.getnext()
                b = a.getnext()
Theresa Pollinger's avatar
Theresa Pollinger committed
120
121
                #print("a: %s - %s - %s" % (a, a.text, a.get('value')))
                #print("b: %s - %s - %s" % (b, b.text, b.get('value')))
Theresa Pollinger's avatar
Theresa Pollinger committed
122
123
                return (a.get('value'), b.get('value'))

124
125
    def tostring(self):
        return element_to_string(self.root)
Theresa Pollinger's avatar
Theresa Pollinger committed
126

127
128
129
130
    def inferred_type_to_string(self):
        type_string = ""
        for mo in self.root.iter("{*}mo"):
            type_string = type_string + " " + mo.text
131
        return type_string.strip()
132

133
134
135

def element_to_string(element):
    return etree.tostring(element, pretty_print=True).decode('utf8')
Theresa Pollinger's avatar
Theresa Pollinger committed
136

137

Theresa Pollinger's avatar
Theresa Pollinger committed
138
139
140
141
142
143
class MMTInterface:
    def __init__(self):
        # set parameters for communication with mmt server
        self.serverInstance = 'http://localhost:8080'
        self.extension = ':interview'
        self.URIprefix = 'http://mathhub.info/'
144
        self.namespace = 'MitM/smglom/calculus'  # TODO
145
        self.debugprint = False
146
147
148
149
#        try:
#            _thread.start_new_thread(run_mmt_server, ())
#        except:
#            print("Error: unable to start mmt thread")
Theresa Pollinger's avatar
Theresa Pollinger committed
150
151

    def mmt_new_theory(self, thyname):
152
153
154
        # So, ich hab mal was zu MMT/devel gepusht. Es gibt jetzt eine Extension namens InterviewServer. Starten tut man die mit "extension info.kwarc.mmt.interviews.InterviewServer"
        # Wenn du dann in MMT den Server (sagen wir auf Port 8080) startest, kannst du folgende HTTP-Requests ausführen:
        # "http://localhost:8080/:interview/new?theory="<MMT URI>"" fügt eine neue theorie mit der uri <MMT URI> hinzu
155
156
        req = '/' + self.extension + '/new?theory=' + quote(self.get_mpath(
            thyname)) + '&meta=' + quote('http://mathhub.info/MitM/Foundation?Logic')
Theresa Pollinger's avatar
Theresa Pollinger committed
157
158
159
160
        return self.http_request(req)

    def mmt_new_view(self, viewname, fromtheory, totheory):
        # analog für ?view="<MMT URI>".
161
162
        req = '/' + self.extension + '/new?view=' + quote(self.get_mpath(viewname)) + '&from=' + quote(self.get_mpath(
            fromtheory)) + '&to=' + quote(self.get_mpath(totheory))
Theresa Pollinger's avatar
Theresa Pollinger committed
163
164
165
        return self.http_request(req)

    def mmt_new_decl(self, declname, thyname, declcontent):
166
        # ".../:interview/new?decl="<irgendwas>"&cont="<MMT URI>" ist der query-path um der theorie <MMT URI> eine neue declaration hinzuzufügen (includes, konstanten...). Die Declaration sollte dabei in MMT-syntax als text im Body des HTTP-requests stehen.
167
        post = '/' + self.extension + '/new?decl=d&cont=' + quote(self.get_mpath(thyname))
Theresa Pollinger's avatar
Theresa Pollinger committed
168
169
170
        return self.http_request(post, add_dd(declcontent))

    def mmt_new_term(self, termname, thyname, termcontent):
171
        # analog für ".../:interview/new?term="<irgendwas>"&cont="<MMT URI>" für terme - nachdem da nicht klar ist was der server damit tun sollte gibt er den geparsten term als omdoc-xml zurück (wenn alles funktioniert)
172
173
174
175
176
        post = '/' + self.extension + '/new?term=' + quote(termname) + '&cont=' + quote(self.get_mpath(thyname))
        return self.http_request(post, termcontent)

    def mmt_infer_type(self, thyname, termcontent):
        post = '/' + self.extension + '/infer?cont=' + quote(self.get_mpath(thyname))
Theresa Pollinger's avatar
Theresa Pollinger committed
177
178
179
        return self.http_request(post, termcontent)

    def http_request(self, message, data=None):
180
        url = self.serverInstance + message
Theresa Pollinger's avatar
Theresa Pollinger committed
181
182
183
184
185
186
187
188
189
190
        print(url) if self.debugprint else 0
        session = requests.Session()
        adapter = requests.adapters.HTTPAdapter()
        session.mount('https://', adapter)
        session.mount('http://', adapter)
        try:
            if data:
                binary_data = data.encode('UTF-8')
                print('\n' + str(data)) if self.debugprint else 0
                headers = {'content-type': 'application/json',
191
                           'content-encoding': 'UTF-8'}
Theresa Pollinger's avatar
Theresa Pollinger committed
192
193
194
                req = session.post(url, data=binary_data, headers=headers, stream=True)
            else:
                req = requests.get(url)
195
        except ConnectionError as error:  # this seems to never be called
Theresa Pollinger's avatar
Theresa Pollinger committed
196
197
198
            print(error)
            print("Are you sure the mmt server is running?")
            raise SystemExit
199
        # print(req.text) if self.debugprint else 0
Theresa Pollinger's avatar
Theresa Pollinger committed
200
201
        if req.text.startswith('<'):
            root = etree.fromstring(req.text)
202
203
        else:
            root = None
Theresa Pollinger's avatar
Theresa Pollinger committed
204
        if req.status_code == 200:
205
206
            return MMTReply(True, root)
        return MMTReply(False, root)
Theresa Pollinger's avatar
Theresa Pollinger committed
207
208

    def get_mpath(self, thyname):
209
        mpath = self.URIprefix + self.namespace + "?" + thyname  # TODO
Theresa Pollinger's avatar
Theresa Pollinger committed
210
211
212
        return mpath

    def query_for(self, thingname):
213
214
        # this here just stolen from what MMTPy does
        # querycontent = b'<function name="presentDecl" param="xml"><literal><uri path="http://mathhub.info/MitM/smglom/algebra?magma"/></literal></function>'
Theresa Pollinger's avatar
Theresa Pollinger committed
215
        querycontent = '<function name="presentDecl" param="xml"><literal><uri path="' + (self.get_mpath(thingname)) + '"/></literal></function>'
216

Theresa Pollinger's avatar
Theresa Pollinger committed
217
218
219
        return self.http_qrequest(querycontent)

    def http_qrequest(self, data, message='/:query'):
220
221
        url = self.serverInstance + message
        print(url) if self.debugprint else 0
Theresa Pollinger's avatar
Theresa Pollinger committed
222
223
224
225
        session = requests.Session()
        adapter = requests.adapters.HTTPAdapter()
        session.mount('https://', adapter)
        session.mount('http://', adapter)
226
        print('\n' + str(data)) if self.debugprint else 0
Theresa Pollinger's avatar
Theresa Pollinger committed
227
228
        binary_data = data.encode('UTF-8')
        headers = {'content-type': 'application/xml'}
229
        req = session.post(url, data=binary_data, headers=headers, stream=True)
Theresa Pollinger's avatar
Theresa Pollinger committed
230
231
        root = etree.fromstring(req.text)
        if req.status_code == 200:
232
233
            return MMTReply(True, root)
        return MMTReply(False, root)
Theresa Pollinger's avatar
Theresa Pollinger committed
234

235

Theresa Pollinger's avatar
Theresa Pollinger committed
236
237
def add_dd(string):
    return string + "❙"