to Bottle: A Python Micro-Framework
Are you looking for a lightweight and simple-to-use web framework for Python? Look no further than Bottle.
This micro-framework allows for quick and easy development of web applications. In this article, we’ll provide an overview of Bottle, go over example code, and explore how to create your own unique application.
Overview of Bottle
Bottle is a Python micro-framework that is great for creating simple web applications. It is lightweight and minimalist, which makes it easy to learn and use.
Bottle uses the WSGI interface and can run on any web server that supports Python. One of the key features of Bottle is its routing system.
Routes determine which function within your application should handle a particular request. The routing system is also what enables dynamic URLs, such as those that include variable parameters.
Example Code
Let’s dive into some example code to see how Bottle compares to other frameworks. Here’s an example of a simple “Hello World” program in Bottle:
Copy
from bottle import route, run
@route('/hello')
def hello():
return "Hello World!"
run(host='localhost', port=8080)
This program starts a Bottle application that binds to localhost on port 8080.
When you visit http://localhost:8080/hello, you will see the text “Hello World!” displayed on your screen.
Writing Your App
Creating the Bottle App
Now that we’ve seen a simple example of a Bottle app, let’s start creating our own. To start, you’ll need to create a new Python file called app.py.
Here’s a basic starter code for that file:
Copy
from bottle import route, run, template
@route('/hello/')
def hello(name):
return template(' Hello {{name}} !', name=name)
run(host='localhost', port=8080)
If you run this code and navigate to http://localhost:8080/hello/yourname, you’ll see the message “Hello yourname!” displayed.
Dynamic Routes
Notice the /
in the route decorator. This indicates a “wildcard” route, where we expect a variable parameter to be passed in.
In this case, the variable is “name”. This parameter is then passed into the hello()
function as an argument.
Let’s explore how to create more complicated dynamic routes. Here’s an example:
Copy
from bottle import route, run
@route('/blog///')
def blog(year, month, title):
return f"{year}, {month}, {title}"
run(host='localhost', port=8080)
When you navigate to http://localhost:8080/blog/2022/08/my-blog-title, you’ll see the text “2022, 08, my-blog-title” displayed.
In this example, the three parameters (year, month, and title) are extracted from the URL and passed as arguments to the blog()
function.
Defining Functions
In all of the previous examples, we’ve defined our functions within the route decorator. However, you can also define your functions outside of the decorator and import them into your app.py file.
Let’s see an example:
Copy
from bottle import route, run
from my_module import my_func
@route('/')
def index():
return my_func()
run(host='localhost', port=8080)
In this example, we’re importing the function my_func()
from a separate module called my_module. We then declare a route with the decorator and call my_func()
within the index()
function.
Conclusion
Bottle is a powerful yet simple micro-framework that makes web development easy and fun. With its minimalist architecture and easy-to-use routing system, developers can easily create web applications in Python.
Whether you’re just starting out with web development or looking for a simple solution to build a web app, Bottle is a great choice. Try out the examples in this article and start building your own Bottle app today!
3) Shell Script
In our previous article, we explored the basic features of Bottle, a Python micro-framework. Now, let’s take it a step further and automate the process of creating a new Bottle application.
We’ll achieve this using a shell script that initializes a new Bottle app along with all the necessary packages. This will save developers time and make the process more efficient.
Generating a Starter App
To generate a starter app using a shell script, we need to follow these steps:
- Create a new directory for the project using the
mkdir
command.
- Inside the new directory, create a new virtual environment using the
virtualenv
command.
- Activate the virtual environment using the
source
command.
- Use
pip
to install the latest version of Bottle.
- Create a new script file using a text editor like
nano
or vim
.
- Inside the script file, add the following code:
> app.py
echo “@route(‘/’)” >> app.py
echo “def index():” >> app.py
echo ” return ‘Hello, World!'” >> app.py
echo “run(host=’localhost’, port=8080)” >> app.py
” aria-label=”Copy” data-copied-text=”Copied!” data-has-text-button=”textSimple” data-inside-header-type=”none” aria-live=”polite”>Copy
#!/bin/bash
echo "Creating new Bottle app"
mkdir static templates views
touch app.py
echo "from bottle import route, run, template" >> app.py
echo "@route('/')" >> app.py
echo "def index():" >> app.py
echo " return 'Hello, World!'" >> app.py
echo "run(host='localhost', port=8080)" >> app.py
The above code creates a basic Bottle app that can be run on localhost:8080. The mkdir
command creates the necessary directories, while the touch
command creates the app.py file.
We then add the required code to the app.py file. Lastly, we need to make the script executable using the chmod
command.
After running the command chmod +x scriptname.sh
, we can then execute the script using the command ./scriptname.sh
. The script will create the necessary files and directories, activate the virtual environment, and install all required packages.
4) Next Steps
Adding New Pages
To add a new page to our app, we use the @route
decorator to create a new route that will be handled by a new function. Underneath the index()
function in our app.py, let’s add a new route and function:
Copy
@route('/about')
def about():
return "This is the About page"
When we route to “localhost:8080/about”, the function about()
is executed and returns the string “This is the About page”.
We can add as many new routes and functions as we need to create a fully-featured web app.
Loading Templates from a File
In our previous example, we simply returned hard-coded text strings. However, web apps are often more complex and require HTML templates to render dynamic content.
In Bottle, we create templates using files and load them into our app. Firstly, we’ll need to create a new folder inside our project directory called views
.
Inside this folder, we create a new file called main_template.tpl
and add some basic HTML code:
const pmDelayClick=false;const pmDelayTimer=setTimeout(pmTriggerDOMListener,10*1000);const pmUserInteractions=["keydown","mousedown","mousemove","wheel","touchmove","touchstart","touchend"],pmDelayedScripts={normal:[],defer:[],async:[]},jQueriesArray=[],pmInterceptedClicks=[];var pmDOMLoaded=!1,pmClickTarget="";function pmTriggerDOMListener(){"undefined"!=typeof pmDelayTimer&&clearTimeout(pmDelayTimer),pmUserInteractions.forEach(function(e){window.removeEventListener(e,pmTriggerDOMListener,{passive:!0})}),document.removeEventListener("visibilitychange",pmTriggerDOMListener),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",pmTriggerDelayedScripts):pmTriggerDelayedScripts()}async function pmTriggerDelayedScripts(){pmDelayEventListeners(),pmDelayJQueryReady(),pmProcessDocumentWrite(),pmSortDelayedScripts(),pmPreloadDelayedScripts(),await pmLoadDelayedScripts(pmDelayedScripts.normal),await pmLoadDelayedScripts(pmDelayedScripts.defer),await pmLoadDelayedScripts(pmDelayedScripts.async),await pmTriggerEventListeners(),document.querySelectorAll("link[data-pmdelayedstyle]").forEach(function(e){e.setAttribute("href",e.getAttribute("data-pmdelayedstyle"))}),window.dispatchEvent(new Event("perfmatters-allScriptsLoaded")),pmReplayClicks()}function pmDelayEventListeners(){let e={};function t(t,r){function n(r){return e[t].delayedEvents.indexOf(r)>=0?"perfmatters-"+r:r}e[t]||(e[t]={originalFunctions:{add:t.addEventListener,remove:t.removeEventListener},delayedEvents:[]},t.addEventListener=function(){arguments[0]=n(arguments[0]),e[t].originalFunctions.add.apply(t,arguments)},t.removeEventListener=function(){arguments[0]=n(arguments[0]),e[t].originalFunctions.remove.apply(t,arguments)}),e[t].delayedEvents.push(r)}function r(e,t){let r=e[t];Object.defineProperty(e,t,{get:r||function(){},set:function(r){e["perfmatters"+t]=r}})}t(document,"DOMContentLoaded"),t(window,"DOMContentLoaded"),t(window,"load"),t(window,"pageshow"),t(document,"readystatechange"),r(document,"onreadystatechange"),r(window,"onload"),r(window,"onpageshow")}function pmDelayJQueryReady(){let e=window.jQuery;Object.defineProperty(window,"jQuery",{get:()=>e,set(t){if(t&&t.fn&&!jQueriesArray.includes(t)){t.fn.ready=t.fn.init.prototype.ready=function(e){pmDOMLoaded?e.bind(document)(t):document.addEventListener("perfmatters-DOMContentLoaded",function(){e.bind(document)(t)})};let r=t.fn.on;t.fn.on=t.fn.init.prototype.on=function(){if(this[0]===window){function e(e){return e=(e=(e=e.split(" ")).map(function(e){return"load"===e||0===e.indexOf("load.")?"perfmatters-jquery-load":e})).join(" ")}"string"==typeof arguments[0]||arguments[0]instanceof String?arguments[0]=e(arguments[0]):"object"==typeof arguments[0]&&Object.keys(arguments[0]).forEach(function(t){delete Object.assign(arguments[0],{[e(t)]:arguments[0][t]})[t]})}return r.apply(this,arguments),this},jQueriesArray.push(t)}e=t}})}function pmProcessDocumentWrite(){let e=new Map;document.write=document.writeln=function(t){var r=document.currentScript,n=document.createRange();let a=e.get(r);void 0===a&&(a=r.nextSibling,e.set(r,a));var i=document.createDocumentFragment();n.setStart(i,0),i.appendChild(n.createContextualFragment(t)),r.parentElement.insertBefore(i,a)}}function pmSortDelayedScripts(){document.querySelectorAll("script[type=pmdelayedscript]").forEach(function(e){e.hasAttribute("src")?e.hasAttribute("defer")&&!1!==e.defer?pmDelayedScripts.defer.push(e):e.hasAttribute("async")&&!1!==e.async?pmDelayedScripts.async.push(e):pmDelayedScripts.normal.push(e):pmDelayedScripts.normal.push(e)})}function pmPreloadDelayedScripts(){var e=document.createDocumentFragment();[...pmDelayedScripts.normal,...pmDelayedScripts.defer,...pmDelayedScripts.async].forEach(function(t){var r=t.getAttribute("src");if(r){var n=document.createElement("link");n.href=r,n.rel="preload",n.as="script",e.appendChild(n)}}),document.head.appendChild(e)}async function pmLoadDelayedScripts(e){var t=e.shift();return t?(await pmReplaceScript(t),pmLoadDelayedScripts(e)):Promise.resolve()}async function pmReplaceScript(e){return await pmNextFrame(),new Promise(function(t){let r=document.createElement("script");[...e.attributes].forEach(function(e){let t=e.nodeName;"type"!==t&&("data-type"===t&&(t="type"),r.setAttribute(t,e.nodeValue))}),e.hasAttribute("src")?(r.addEventListener("load",t),r.addEventListener("error",t)):(r.text=e.text,t()),e.parentNode.replaceChild(r,e)})}async function pmTriggerEventListeners(){pmDOMLoaded=!0,await pmNextFrame(),document.dispatchEvent(new Event("perfmatters-DOMContentLoaded")),await pmNextFrame(),window.dispatchEvent(new Event("perfmatters-DOMContentLoaded")),await pmNextFrame(),document.dispatchEvent(new Event("perfmatters-readystatechange")),await pmNextFrame(),document.perfmattersonreadystatechange&&document.perfmattersonreadystatechange(),await pmNextFrame(),window.dispatchEvent(new Event("perfmatters-load")),await pmNextFrame(),window.perfmattersonload&&window.perfmattersonload(),await pmNextFrame(),jQueriesArray.forEach(function(e){e(window).trigger("perfmatters-jquery-load")});let e=new Event("perfmatters-pageshow");e.persisted=window.pmPersisted,window.dispatchEvent(e),await pmNextFrame(),window.perfmattersonpageshow&&window.perfmattersonpageshow({persisted:window.pmPersisted})}async function pmNextFrame(){return new Promise(function(e){requestAnimationFrame(e)})}function pmClickHandler(e){e.target.removeEventListener("click",pmClickHandler),pmRenameDOMAttribute(e.target,"pm-onclick","onclick"),pmInterceptedClicks.push(e),e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation()}function pmReplayClicks(){window.removeEventListener("touchstart",pmTouchStartHandler,{passive:!0}),window.removeEventListener("mousedown",pmTouchStartHandler),pmInterceptedClicks.forEach(e=>{e.target.outerHTML===pmClickTarget&&e.target.dispatchEvent(new MouseEvent("click",{view:e.view,bubbles:!0,cancelable:!0}))})}function pmTouchStartHandler(e){"HTML"!==e.target.tagName&&(pmClickTarget||(pmClickTarget=e.target.outerHTML),window.addEventListener("touchend",pmTouchEndHandler),window.addEventListener("mouseup",pmTouchEndHandler),window.addEventListener("touchmove",pmTouchMoveHandler,{passive:!0}),window.addEventListener("mousemove",pmTouchMoveHandler),e.target.addEventListener("click",pmClickHandler),pmRenameDOMAttribute(e.target,"onclick","pm-onclick"))}function pmTouchMoveHandler(e){window.removeEventListener("touchend",pmTouchEndHandler),window.removeEventListener("mouseup",pmTouchEndHandler),window.removeEventListener("touchmove",pmTouchMoveHandler,{passive:!0}),window.removeEventListener("mousemove",pmTouchMoveHandler),e.target.removeEventListener("click",pmClickHandler),pmRenameDOMAttribute(e.target,"pm-onclick","onclick")}function pmTouchEndHandler(e){window.removeEventListener("touchend",pmTouchEndHandler),window.removeEventListener("mouseup",pmTouchEndHandler),window.removeEventListener("touchmove",pmTouchMoveHandler,{passive:!0}),window.removeEventListener("mousemove",pmTouchMoveHandler)}function pmRenameDOMAttribute(e,t,r){e.hasAttribute&&e.hasAttribute(t)&&(event.target.setAttribute(r,event.target.getAttribute(t)),event.target.removeAttribute(t))}window.addEventListener("pageshow",e=>{window.pmPersisted=e.persisted}),pmUserInteractions.forEach(function(e){window.addEventListener(e,pmTriggerDOMListener,{passive:!0})}),pmDelayClick&&(window.addEventListener("touchstart",pmTouchStartHandler,{passive:!0}),window.addEventListener("mousedown",pmTouchStartHandler)),document.addEventListener("visibilitychange",pmTriggerDOMListener);var pmeDeviceMode,pmeAnimationSettingsKeys,pmeCurrentAnimation;function pmeAnimation(){(pmeDeviceMode=document.createElement("span")).id="elementor-device-mode",pmeDeviceMode.setAttribute("class","elementor-screen-only"),document.body.appendChild(pmeDeviceMode),requestAnimationFrame(pmeDetectAnimations)}function pmeDetectAnimations(){pmeAnimationSettingsKeys=pmeListAnimationSettingsKeys(getComputedStyle(pmeDeviceMode,":after").content.replace(/"/g,"")),document.querySelectorAll(".elementor-invisible[data-settings]").forEach(a=>{let b=a.getBoundingClientRect();if(b.bottom>=0&&b.top<=window.innerHeight)try{pmeAnimateElement(a)}catch(c){}})}function pmeAnimateElement(a){let b=JSON.parse(a.dataset.settings),d=b._animation_delay||b.animation_delay||0,c=b[pmeAnimationSettingsKeys.find(a=>b[a])];if("none"===c)return void a.classList.remove("elementor-invisible");a.classList.remove(c),pmeCurrentAnimation&&a.classList.remove(pmeCurrentAnimation),pmeCurrentAnimation=c;let e=setTimeout(()=>{a.classList.remove("elementor-invisible"),a.classList.add("animated",c),pmeRemoveAnimationSettings(a,b)},d);window.addEventListener("perfmatters-startLoading",function(){clearTimeout(e)})}function pmeListAnimationSettingsKeys(b="mobile"){let a=[""];switch(b){case"mobile":a.unshift("_mobile");case"tablet":a.unshift("_tablet");case"desktop":a.unshift("_desktop")}let c=[];return["animation","_animation"].forEach(b=>{a.forEach(a=>{c.push(b+a)})}),c}function pmeRemoveAnimationSettings(a,b){pmeListAnimationSettingsKeys().forEach(a=>delete b[a]),a.dataset.settings=JSON.stringify(b)}document.addEventListener("DOMContentLoaded",pmeAnimation)