2
2
3
3
import errno
4
4
import http .server
5
- import importlib . util
5
+ import importlib
6
6
import json
7
+ import logging
7
8
import os
8
9
import random
9
10
import re
10
11
import socket
11
12
import socketserver
12
- import sys
13
13
import threading
14
14
import time
15
15
import urllib .parse
16
16
import webbrowser
17
17
18
18
__version__ = "0.0.0"
19
19
20
+ logger = logging .getLogger (__name__ )
21
+
20
22
class _ContentProvider :
21
23
data = bytearray ()
22
24
base_dir = ""
@@ -43,7 +45,6 @@ def read(self, path):
43
45
44
46
class _HTTPRequestHandler (http .server .BaseHTTPRequestHandler ):
45
47
content = None
46
- verbosity = 1
47
48
mime_types = {
48
49
".html" : "text/html" ,
49
50
".js" : "text/javascript" ,
@@ -107,10 +108,9 @@ def do_GET(self):
107
108
content = re .sub (regex , lambda _ : meta , content )
108
109
content = content .encode ("utf-8" )
109
110
status_code = 200
110
- _log (self .verbosity > 1 , f"{ str (status_code )} { self .command } { self .path } \n " )
111
111
self ._write (status_code , content_type , content )
112
112
def log_message (self , format , * args ):
113
- return
113
+ logger . debug ( " " . join ( args ))
114
114
def _write (self , status_code , content_type , content ):
115
115
self .send_response (status_code )
116
116
if content :
@@ -127,16 +127,14 @@ class _ThreadedHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
127
127
pass
128
128
129
129
class _HTTPServerThread (threading .Thread ):
130
- def __init__ (self , content , address , verbosity ):
130
+ def __init__ (self , content , address ):
131
131
threading .Thread .__init__ (self )
132
- self .verbosity = verbosity
133
132
self .address = address
134
133
self .url = "http://" + address [0 ] + ":" + str (address [1 ])
135
134
self .server = _ThreadedHTTPServer (address , _HTTPRequestHandler )
136
135
self .server .timeout = 0.25
137
136
self .server .block_on_close = False
138
137
self .server .RequestHandlerClass .content = content
139
- self .server .RequestHandlerClass .verbosity = verbosity
140
138
self .terminate_event = threading .Event ()
141
139
self .terminate_event .set ()
142
140
self .stop_event = threading .Event ()
@@ -155,10 +153,10 @@ def run(self):
155
153
def stop (self ):
156
154
""" Stop server """
157
155
if self .alive ():
158
- _log ( self . verbosity > 0 , "Stopping " + self .url + " \n " )
156
+ logger . info ( "Stopping " + self .url )
159
157
self .stop_event .set ()
160
158
self .server .server_close ()
161
- self .terminate_event .wait (1000 )
159
+ self .terminate_event .wait (1 )
162
160
163
161
def alive (self ):
164
162
""" Check server status """
@@ -198,11 +196,6 @@ def _threads(address=None):
198
196
threads = [ _ for _ in threads if address [1 ] == _ .address [1 ] ]
199
197
return threads
200
198
201
- def _log (condition , message ):
202
- if condition :
203
- sys .stdout .write (message )
204
- sys .stdout .flush ()
205
-
206
199
def _make_address (address ):
207
200
if address is None or isinstance (address , int ):
208
201
port = address
@@ -253,13 +246,13 @@ def stop(address=None):
253
246
for thread in threads :
254
247
thread .stop ()
255
248
256
- def status (adrress = None ):
249
+ def status (address = None ):
257
250
"""Is model served at address.
258
251
259
252
Args:
260
253
address (tuple, optional): A (host, port) tuple, or a port number.
261
254
"""
262
- threads = _threads (adrress )
255
+ threads = _threads (address )
263
256
return len (threads ) > 0
264
257
265
258
def wait ():
@@ -268,32 +261,31 @@ def wait():
268
261
while len (_threads ()) > 0 :
269
262
time .sleep (0.1 )
270
263
except (KeyboardInterrupt , SystemExit ):
271
- _log ( True , " \n " )
264
+ logger . info ( " " )
272
265
stop ()
273
266
274
- def serve (file , data = None , address = None , browse = False , verbosity = 1 ):
267
+ def serve (file , data = None , address = None , browse = False ):
275
268
"""Start serving model from file or data buffer at address and open in web browser.
276
269
277
270
Args:
278
271
file (string): Model file to serve. Required to detect format.
279
272
data (bytes): Model data to serve. None will load data from file.
280
273
address (tuple, optional): A (host, port) tuple, or a port number.
281
274
browse (bool, optional): Launch web browser. Default: True
282
- log (bool, optional): Log details to console. Default: False
283
275
284
276
Returns:
285
277
A (host, port) address tuple.
286
278
"""
287
- verbosities = { "0" : 0 , "quiet" : 0 , "1" : 1 , "default" : 1 , "2" : 2 , "debug" : 2 }
288
- verbosity = verbosities [ str ( verbosity )]
279
+ if not logging . getLogger (). hasHandlers ():
280
+ logging . basicConfig ( level = logging . INFO , format = "%(message)s" )
289
281
290
282
if not data and file and not os .path .exists (file ):
291
283
raise FileNotFoundError (errno .ENOENT , os .strerror (errno .ENOENT ), file )
292
284
293
285
content = _ContentProvider (data , file , file , file )
294
286
295
287
if data and not isinstance (data , bytearray ) and isinstance (data .__class__ , type ):
296
- _log ( verbosity > 1 , "Experimental\n " )
288
+ logger . info ( "Experimental" )
297
289
model = _open (data )
298
290
if model :
299
291
text = json .dumps (model .to_json (), indent = 2 , ensure_ascii = False )
@@ -305,31 +297,29 @@ def serve(file, data=None, address=None, browse=False, verbosity=1):
305
297
else :
306
298
address = _make_port (address )
307
299
308
- thread = _HTTPServerThread (content , address , verbosity )
300
+ thread = _HTTPServerThread (content , address )
309
301
thread .start ()
310
302
while not thread .alive ():
311
303
time .sleep (0.01 )
312
304
state = ("Serving '" + file + "'" ) if file else "Serving"
313
- message = f"{ state } at { thread .url } \n "
314
- _log (verbosity > 0 , message )
305
+ logger .info (f"{ state } at { thread .url } " )
315
306
if browse :
316
307
webbrowser .open (thread .url )
317
308
318
309
return address
319
310
320
- def start (file = None , address = None , browse = True , verbosity = 1 ):
311
+ def start (file = None , address = None , browse = True ):
321
312
"""Start serving model file at address and open in web browser.
322
313
323
314
Args:
324
315
file (string): Model file to serve.
325
- log (bool, optional): Log details to console. Default: False
326
316
browse (bool, optional): Launch web browser, Default: True
327
317
address (tuple, optional): A (host, port) tuple, or a port number.
328
318
329
319
Returns:
330
320
A (host, port) address tuple.
331
321
"""
332
- return serve (file , None , browse = browse , address = address , verbosity = verbosity )
322
+ return serve (file , None , browse = browse , address = address )
333
323
334
324
def widget (address , height = 800 ):
335
325
""" Open address as Jupyter Notebook IFrame.
0 commit comments