How to start Bottle as a daemon from another script? - python

I would like to use BottlePy as a daemon started from another script and I have issues turning a standalone script (webserver.py) into a class. The standalone version of my webserver below works fine:
import bottle
#bottle.get('/hello')
def hello():
return 'Hello World'
#bottle.error(404)
def error404(error):
return 'error 404'
bottle.run(host='localhost', port=8080)
My intent is now to start it from the main script below as
from webserver import WebServer
from multiprocessing import Process
def start_web_server():
# initialize the webserver class
WebServer()
# mainscript.py operations
p = Process(target=start_web_server)
p.daemon = True
p.start()
# more operations
where WebServer() would be in the naively now-modified webserver.py:
import bottle
class WebServer():
def __init__(self):
bottle.run(host='localhost', port=8080)
#bottle.get('/hello')
def hello(self):
return 'Hello World'
#bottle.error(404)
def error404(self, error):
return 'error 404'
What works: the whole thing starts and the webserver is listening
What does not work: upon calling http://localhost:8080/hello
127.0.0.1 - - [11/Dec/2013 10:16:23] "GET /hello HTTP/1.1" 500 746
Traceback (most recent call last):
File "C:\Python27\lib\site-packages\bottle.py", line 764, in _handle
return route.call(**args)
File "C:\Python27\lib\site-packages\bottle.py", line 1575, in wrapper
rv = callback(*a, **ka)
TypeError: hello() takes exactly 1 argument (0 given)
My questions are:
what kind of parameters am I expected to pass to hello() and error404()?
what should I do in order to parametrize #bottle.get('/hello')? I would like to have something like #bottle.get(hello_url) but where should hello_url = '/hello' be initialized? (self.hello_url is unknown to #bottle.get)
EDIT: while preparing a fork of this question to handle question 2 (about the parametrization) I had an epiphany and tried the obvious solution which works (code below). I am not that used to classes yet so I did not have the reflex to add the variable in the scope of the class.
# new code with the path as a parameter
class WebServer():
myurl = '/hello'
def __init__(self):
bottle.run(host='localhost', port=8080, debug=True)
#bottle.get(myurl)
def hello():
return 'Hello World'
#bottle.error(404)
def error404(error):
return 'error 404'

what kind of parameters am I expected to pass to hello() and error404()?
The short answer: none. Just remove self and they should start to work.
#bottle.get('/hello')
def hello():
return 'Hello World'
#bottle.error(404)
def error404(error):
return 'error 404'
what should I do in order to parametrize #bottle.get('/hello')? I
would like to have something like #bottle.get(hello_url) but where
should hello_url = '/hello' be initialized? (self.hello_url is unknown
to #bottle.get)
I can interpret this a couple of different ways, so I'm not sure how to help you. But since this is a completely separate question (with potentially a much larger scope), please consider asking it in a new, separate SO question.

Related

How does flask know which function to call for a specific web adress?

In the populair web framework flask a basic web page looks like this:
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return "Hello World!"
I am pretty new to python and i was wondering how this exactly works. I get that # is a decorator that decorates the hello function but how does flask that is has to call the underlying hello funtion or even knows it exists, because the code does not run the hello function like this:
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return "Hello World!"
hello()
When i am coding i like to know how something works before i just randomly accept anything. I searched my but off looking for an answer but could not find a pleasent answer. Also i looked in the source code but i was not able to find out how it works
So now the real question: How can i recreate something similair in plain python? So running a function without really calling it in the main code first.
Ps. Sorry for my bad english, it is not my main language.
app.route() remembers the URL ("/") and the function associated with it (hello). Later, app.run() can query that association and invoke hello.
How can i recreate something similair in plain python?
This program might give you an understanding of how hello() is invoked:
class Flask:
def __init__(self):
self.routes = {}
def route(self, path):
def wrapper(fn):
self.routes[path] = fn
return fn
return wrapper
def run(self):
# Networking code goes here.
# Suppose "/" comes in as a request, then this happens:
self.routes["/"]()
app = Flask()
#app.route("/")
def hello():
print("Inside hello")
return "Hello World!"
app.run()
Alternatively, you can examine the flask source: https://github.com/pallets/flask Specifically, app.route() is defined here: https://github.com/pallets/flask/blob/0.12.2/flask/app.py#L1054 and the call to hello() is here: https://github.com/pallets/flask/blob/0.12.2/flask/app.py#L1052

How to suppress web.py output?

I am trying to suppress the web.py output from the console. I do not wish to suppress all output to stderr or redirect the output, but merely stop it from printing statements such as the following to the console.
127.0.0.1:57691 - - [06/Mar/2018 22:25:43] "HTTP/1.1 GET /api/getDimensionColumnConstraints" - 200 OK
I have tried adding the web.config.debug = False statement as the first line in my main() function and it still prints this for every API call.
The built-in server for web.py sends the HTTP info and other errors to stderr by default. I'm with you -- I want the errors, but don't want all the HTTP statements.
So, here's what you can do.
Create a custom Logger
Intercept calls within the logger, tossing out log records matching the pattern.
Not so hard, you'll create a basic wsgi application middleware to intercept:
import logging
class Log:
def __init__(self, xapp, logname="wsgi"):
class O:
def __init__(self, xapp, logname="wsgi"):
self.logger = logging.getLogger(logname)
def write(self, s):
if s[-1] == '\n':
s = s[:-1]
if s == "":
return
if self.ignore(s):
return
self.logger.debug(s)
self.app = xapp
self.f = O(logname)
def __call__(self, environ, start_response):
environ['wsgi.errors'] = self.f
return self.app(environ, start_response)
Add that middleware into your __main__ initialization of the application. You probably have something like:
if __name__ == '__main__':
app = web.application(urls, globals())
app.run()
Change the run() to add the middleware:
app.run(Log)
Now the second part. See the self.ignore(s) within O.write() above? Use that to decide to process or ignore the logging statement, for example I do:
def ignore(self, s):
if not all([web.config.get('debug_http', False),
any(['"HTTP/1.1 GET ' in s,
'"HTTP/1.1 POST ' in s])]):
return True
return False
Which basically says if the string to print contains 'HTTP...' ignore it. I've added the check to a global flag 'debug_http', which allows me to set that (somewhere, anywhere, anyhow) to enable / disable this item.

Declaration of CherryPy Tool fails

I'm trying to declare a new tool to the CherryPy toolbox following the examples from the docs: Docs CherryPy Tools.
According to the examples I have written:
import cherrypy
def myTool():
print ("myTool")
class Root(object):
#cherrypy.expose
#cherrypy.tools.mytool()
def index(self):
return "Hello World!"
if __name__ == '__main__':
cherrypy.tools.mytool = cherrypy.Tool('before_finalize', myTool)
cherrypy.quickstart(Root(), '/')
This results in the following error:
Traceback (most recent call last):
File "server.py", line 6, in <module>
class Root(object):
File "server.py", line 8, in Root
#cherrypy.tools.mytool()
AttributeError: 'Toolbox' object has no attribute 'mytool'
However if I change the notation to the following it works as expected.
import cherrypy
def myTool():
print ("myTool")
class Root(object):
#cherrypy.expose
def index(self):
return "Hello World!"
index._cp_config = {'tools.mytool.on': True}
if __name__ == '__main__':
cherrypy.tools.mytool = cherrypy.Tool('before_finalize', myTool)
cherrypy.quickstart(Root(), '/')
The docs says that both methods have the same effect, but not in my case. If anyone knows what I'm doing wrong I'll be very grateful.
The tool should not be defined globally, hence the #cherrypy.tools.mytool() notation.
I'm using python 3.6.
The problem is the misunderstanding of the evaluation order of python (top-down), at the time the class is defined the tool has not been defined.
You can define the tool in another file import at the top (before the class definition) and it should work.
The second form works, because the configuration is done indirectly using strings in the config, not the real tool objects.

python- split() doesn't work inside __init__

I am writing code to serve a html file using wsgi.When I write a straight forward function I get no error like :
from wsgiref.simple_server import make_server
import os
...
...
def app(environ, start_response):
path_info = environ["PATH_INFO"]
resource = path_info.split("/")[1] #I get no error here the split works totally fine.
Now when I try to put the code inside a class I get error NoneType has no attribute split.
Perhaps the environ inside __init__ doesn't get initialised , that's why it split returns nothing. Following is the file in which my class Candy resides :
import os
class Candy:
def __init__(self):
#self.environ = environ
#self.start = start_response
self.status = "200 OK"
self.headers = []
def __call__(self , environ , start_response):
self.environ = environ
self.start = start_response
#headers = []
def content_type(path):
if path.endswith(".css"):
return "text/css"
else:
return "text/html"
def app(self):
path_info = self.environ["PATH_INFO"]
resource = path_info.split("/")[1]
#headers = []
self.headers.append(("Content-Type", content_type(resource)))
if not resource:
resource = "login.html"
resp_file = os.path.join("static", resource)
try:
with open(resp_file, "r") as f:
resp_file = f.read()
except Exception:
self.start("404 Not Found", self.headers)
return ["404 Not Found"]
self.start("200 0K", self.headers)
return [resp_file]
Following is the server.py file where I invoke my make_server :
from wsgiref.simple_server import make_server
from candy import Candy
#from app import candy_request
candy_class = Candy()
httpd = make_server('localhost', 8000, candy_class.app)
print "Serving HTTP on port 8000..."
# Respond to requests until process is killed
httpd.serve_forever()
# Alternative: serve one request, then exit
#httpd.handle_request()
Any help ? How to get this error sorted and am I right in my assumption?
To explain what you're doing wrong here, let's start with simple concepts - what a WSGI application is.
WSGI application is just a callable that receives a request environment, and a callback function that starts a response (sends status line and headers back to user). Then, this callable must return one or more strings, that constitute the response body.
In the simplest form, that you have working it's just
def app(environ, start_response):
start_response("200 OK", [("Content-Type", "text/plain")])
return "hello, world"
make_server('localhost', 8000, app).serve_forever()
Whenever a request comes, app function gets called, it starts the response and returns a string (or it could return an iterable of multiple strings, e.g. ["hello, ", "world"])
Now, if you want it to be a class, it works like this:
class MyApp(object):
def __init__(self):
pass
def __call__(self, environ, start_response):
start_response("200 OK", [("Content-Type", "text/plain")])
return "something"
app = MyApp()
make_server("localhost", 8000, app).serve_forever()
In this case, the callable is app, and it's actually __call__ method of Caddy class instance.
When request comes, app.__call__ gets called (__call__ is the magic method that turns your class instance in a callable), and otherwise it works exactly the same as the app function from the first example. Except that you have a class instance (with self), so you can do some pre-configuration in the __init__ method. Without doing anything in __init__ it's useless. E.g., a more realistic example would be this:
class MyApp(object):
def __init__(self):
self.favorite_color = "blue"
def __call__(self, environ, start_response):
start_response("200 OK", [("Content-Type", "text/plain")])
return "my favorite color is {}".format(self.favorite_color)
...
Then, there's another thing. Sometimes you want a streaming response, generated over time. Maybe it's big, or maybe it takes a while. That's why WSGI applications can return an iterable, rather than just a string.
def app(environ, start_response):
start_response("200 OK", [("Content-Type", "text/plain")]))
yield "This was a triumph\n"
time.sleep(1)
yield "I'm making a note here\n"
time.sleep(1)
yield "HUGE SUCCESS\n"
make_server("localhost", 8000, app).serve_forever()
This function returns a generator that returns text, piece by piece. Although your browser may not always show it like this, but try running curl http://localhost:8000/.
Now, the same with classes would be:
class MyApp(object):
def __init__(self, environ, start_response):
self.environ = environ
self.start = start_response
def __iter__(self):
self.start("200 OK", [("Content-Type", "text/plain")]))
yield "This was a triumph\n"
time.sleep(1)
yield "I'm making a note here\n"
time.sleep(1)
yield "HUGE SUCCESS\n"
make_server("localhost", 8000, MyApp).serve_forever()
Here, you pass MyApp (the class) as a application callable - which it is. When request comes it gets called, it's like someone had written MyApp(environ, start_response) somewhere, so __init__ starts and creates an instance for this specific request. Then, as the instance is iterated, __iter__ starts to produce a response. After it's done, the instance is discarded.
Basically, that's it. Classes here are only convenience closures that hold data. If you don't need them, don't use classes, use plain functions - flat is better than nested.
Now, about your code.
What your code uses for callable is Candy().app. This doesn't work because it doesn't even made to receive environ and start_response it will be passed. It should probably fail with 500 error, saying something like app() takes 1 positional arguments but 3 were given.
I assume the code in your question is modified after you got that NoneType has no attribute split issue, and you had passed something to the __init__ when creating candy_instance = Candy() when your __init__ still had 2 arguments (3 with self). Not even sure what exactly it was - it should've failed earlier.
Basically, you passed the wrong objects to make_server and your class was a mix of two different ideas.
I suggest to check my examples above (and read PEP-333), decide what you actually need, and structure your Candy class like that.
If you just need to return something on every request, and you don't have a persistent state - you don't need a class at all.
If you need a persistent state (config, or, maybe, a database connection) - use a class instance, with __call__ method, and make that __call__ return the response.
If you need to respond in chunks, use either a generator function, or a class with __iter__ method. Or a class with __call__ that yields (just like a function).
Hope this helps.

How to use decorators (bottle.py)

I am trying to use bottle.py to build some webpages. It seems like a major part of using bottle is learning to use decorators but I have read the python docs explanation of what decorators are but I am still not sure I understand them.
The docs say:
"A Python decorator is a specific change to the Python syntax that allows us to more conveniently alter functions and methods (and possibly classes in a future version)."
It sounds like you are calling a function with some changes made but I am not sure why you would do it this way or how to read the decorator.
Looking at some bottle code:
if __name__ == '__main__':
PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static').replace('\\', '/')
HOST = os.environ.get('SERVER_HOST', 'localhost')
try:
PORT = int(os.environ.get('SERVER_PORT', '5555'))
except ValueError:
PORT = 5555
#bottle.route('/static/<filepath:path>')
def server_static(filepath):
"""Handler for static files, used with the development server.
When running under a production server such as IIS or Apache,
the server should be configured to serve the static files."""
return bottle.static_file(filepath, root=STATIC_ROOT)
# Starts a local test server.
bottle.run(server='wsgiref', host=HOST, port=PORT)
What does this line do #bottle.route('/static/<filepath:path>')?
If its a fancy function call then why do it this way rather than just calling the function?
Thanks for your help! :D
Check out this code:
def my_decorator(func):
return lambda: print("goodbye")
def greet():
print('hello')
result = my_decorator(greet)
result()
--output:--
goodbye
The following is a shortcut to accomplish the same thing:
def my_decorator(func):
return lambda: print("goodbye")
#my_decorator
def greet():
print('hello')
greet()
--output:--
goodbye
The #my_decorator syntax takes the function below it, greet, and makes this call:
greet = my_decorator(greet)
The my_decorator() function has to be defined so that:
It takes a function as an argument.
Returns a function.
A Python decorator is a specific change to the Python syntax that
allows us to more conveniently alter functions and methods (and
possibly classes in a future version).
Okay, so let's say that you want to add to whatever the greet() function does:
def my_decorator(func): # func = greet
def add_to_greet():
func() #<*********This is greet()
print('world') #<***This is additional stuff.
return add_to_greet
#my_decorator
def greet():
print('hello')
greet()
--output:--
hello
world
What does this line do #bottle.route('/static/<filepath:path>')
Okay, are you ready? If the #some_name syntax specifies an argument, for instance:
#wrapper('world')
def do_stuff():
First python will execute the following call:
#wrapper('world')
def do_stuff():
...
#****HERE:
decorator = wrapper('world') #decorator is a newly created variable
The wrapper() function must be defined to:
Take any old argument, e.g. 'world'
Return a function that:
Takes a function as an argument.
Returns a function.
Secondly, python will execute the call:
#wrapper('world')
def do_stuff():
...
decorator = wrapper('world')
#*****HERE:
do_stuff = decorator(do_stuff)
Whew! Here is an example:
def wrapper(extra_greeting):
def my_decorator(func):
def add_to_greet():
func()
print(extra_greeting)
return add_to_greet
return my_decorator
#wrapper('world')
def greet():
print('hello')
greet()
--output:--
hello
world
Now, let's analyze this decorator:
#bottle.route('/static/<filepath:path>')
def server_static(filepath):
bottle -- a module
route -- a function(or other callable) defined in the bottle module
'/static/<filepath:path>' -- a route
So the bottle module might look like this:
#bottle.py
def route(your_route): #your_route <= '/static/<filepath:path>'
def my_decorator(func): #The decorator syntax will cause python to call this function with server_static as the argument
def do_stuff(filepath):
func(filepath) #Call the server_static() function with the part of the url that matched filepath
return do_stuff #This function will be called when your code calls server_static(...)
return my_decorator
If its a fancy function call then why do it this way rather than just
calling the function?
Advanced stuff.
Comment: Perhaps you forgot to explain what specifically that route decorator does?
#route('/hello')
def hello():
return "Hello World!"
The route() decorator binds a piece of code to an URL path. In this
case, we link the /hello path to the hello() function. This is called
a route (hence the decorator name) and is the most important concept
of this framework. You can define as many routes as you want. Whenever
a browser requests a URL, the associated function is called and the
return value is sent back to the browser. It’s as simple as that.
http://bottlepy.org/docs/dev/tutorial.html
A path can include wild cards:
The simplest form of a wildcard consists of a name enclosed in angle
brackets (e.g. <name>)....Each wildcard matches one or more
characters, but stops at the first slash (/).
The rule /<action>/<item> matches as follows:
Path Result
/save/123 {'action': 'save', 'item': '123'}
/save/123/ No Match
/save/ No Match
//123 No Match
Filters are used to define more specific wildcards, and/or transform
the matched part of the URL before it is passed to the callback. A
filtered wildcard is declared as <name:filter>
The following standard filters are implemented:
:path matches all characters including the slash character in a non-greedy way and may be used to match more than one path
segment.
http://bottlepy.org/docs/dev/routing.html
A decorator is just a function wrapper, it takes some computable and surround it with more computables, technically a decorators is a function that returns a function(or an object, in fact there are decorator classes).
Lets say for example you want to make a logger decorator, this logger decorator will execute something and log who is executing it:
def loggger(name):
def wrapper(f):
def retF(*args, **kwargs):
print name, "is executing"
f(*args, **kwargs)
return retF
return wrapper
So, we have our decorator that will print "Daniel is executing" before call our desired function, for example
#logger("Daniel")
def add2Nums(a,b):
return a+b
>>> add2Nums(1,2)
>>> Daniel is executing
>>> 3
Bottle just works the same way, in the
#bottle.route('/static/<filepath:path>')
it just wrapps your server_static call so whenever some acces that route your function is called.

Categories