HTML file response in Pyramid - python

I am trying to adapt the to-do list app in the Pyramid tutorial here to create a Python hangman game. My directory structure is as follows:
/tasks
tasks.py
/static
custom.css
/templates
main.html
I want to have a single view main.html (i.e., just the game title, the current picture with blanks, score, etc., and a button for each letter to guess) that gets updated every time the user chooses a letter.
I figured I could do this by dynamically creating a new HTML display with each button press, similar to the jpg serving method shown here. But upon running python tasks.py and opening http://localhost:8080/# in the browser, it gives a server error and: "OSError: [Errno 2] No such file or directory: '/templates/main.html' in the terminal.
Here's my tasks.py:
import os
import logging
from pyramid.config import Configurator
from pyramid.events import NewRequest
from pyramid.events import ApplicationCreated
from pyramid.exceptions import NotFound
from pyramid.httpexceptions import HTTPFound
from pyramid.response import FileResponse
from pyramid.view import view_config
from wsgiref.simple_server import make_server
logging.basicConfig()
log = logging.getLogger(__file__)
here = os.path.dirname(os.path.abspath(__file__))
##view_config(route_name='home', renderer='main.mako')
#def main_page(request):
# print request #just checking
# return {}
#view_config(route_name='home')
def main_page(request):
response = FileResponse(os.path.join(here,'/templates/main.html'), \
request=request, content_type='text/html')
return response
if __name__ == '__main__':
settings = {}
settings['reload_all'] = True
settings['debug_all'] = True
settings['mako.directories'] = os.path.join(here, 'templates')
config = Configurator(settings=settings)
config.include('pyramid_mako')
config.add_route('home', '/')
config.add_static_view('static', os.path.join(here, 'static'))
config.scan()
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 8080, app)
print 'server made'
server.serve_forever()
Considering the here declaration, I don't see how the file can't be found. As you can see from the commented out view_config, I tried using my initial HTML file (renamed main.mako) as the renderer at first, which worked fine and displayed the main page nicely.
But I don't think I can use that to update dynamically as desired. What am I missing here? Maybe I need to add something else to the settings dictionary?

You getting this error because you are using absolute path /templates/main.html.
Simple example how os.path.join works:
>>> os.path.join('/some/path/to/project', '/templates/main.html')
'/templates/main.html'
>>> os.path.join('/some/path/to/project', 'templates/main.html')
'/some/path/to/project/templates/main.html'
So try change os.path.join(here,'/templates/main.html') to os.path.join(here,'templates/main.html').

Related

How to remove older css files after webassets rebuilds the scss for a python flask app?

I have a Python 3.9 flask app which uses the flask_assets library.
My flask init.py file looks like:
import logging
import os
from flask import Flask, request, current_app
from config import Config
from flask_assets import Environment
from app.utils.assets import bundles
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
assets = Environment(app)
assets.debug = True
assets.versions = 'timestamp'
# assets.cache = False
from app.main import bp as main_bp
app.register_blueprint(main_bp)
assets.register(bundles)
if not app.debug and not app.testing:
app.logger.setLevel(logging.INFO)
app.logger.info('Application starting.')
return app
Since flask_assets is built on top of webassets, I import the Environment and a css Bundle I created which compiles my scss code to css.
Here is how my Bundle looks like:
from flask_assets import Bundle
bundles = {
'css': Bundle (
'scss/_main.scss',
'scss/_base.scss',
'scss/_typography.scss',
'scss/_page_home.scss',
'scss/_page_technote.scss',
filters='pyscss',
depends=('**/*.scss'),
output='css/style.%(version)s.scss.css'
)
}
The problem I have:
Every time I make a change to my scss files, the css successfully rebuilds with a new version for cache busting. However, the older css files remain.
What's the best automatic way to remove them every time a rebuild happens? Is there any reason for keeping the older files?
Also - side question - is it possible for the Bundle object to automatically consider all files of certain type in a directory? Rather than me listing every file individually?
Here is how my files look like:
Thank you!
You can use rmtree. Even though I believe a better way exists, this does it.
from os.path import join
from shutil import rmtree
app = Flask(__name__)
rmtree(join(app.static_folder, 'css'), ignore_errors=True)
You can improve this by checking the file meta by date or similar to not delete the unchanged ones.

Config parameter not reading from other locations

The parameter is not read from files other than the run.py . run.py is the Flask startup script
from run.py :
from flask import Flask
app = Flask(__name__)
print(app.config["PARAMETER"])
From any other python file in project following error is returned :
KeyError: 'PARAMETER'
How to read configuration file from other locations within project other than startup script file ?
Update :
Reading config file :
app.config.from_pyfile(os.path.join(".", "myconfig.conf"))
Contents of myconfig.conf :
PARAMETER = "test"
I think you just simply missed loading parameters.
If you do inline loadign of parameters, you would specifiy it as:
from flask import Flask
app = Flask(__name__)
app.config['PARAMETER_1'] = 'test'
assert app.config["PARAMETER_1"] == 'test'
The code above works, now how can one load similar data from file?
# prepare file
from pathlib import Path
Path("myconfig.conf").write_text("PARAMETER_2 = 'test2'\n")
# load it
app.config.from_pyfile("myconfig.conf")
# test it
assert app.config["PARAMETER_2"] == 'test2'
You can't print the PARAMETER key after the creation of the app because there is no any key into its configuration object which its name is PARAMETER. In order to do it, first create the app, import a simple object which holds the configuration and then use app.config.from_object(Config)
#config.py
class Config(object):
PARAMETER = 'my-parameter-value'
#you can add another keys
SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'
#__init__.py
from config import Config
app = Flask(__name__);
app.config.from_object(Config)

How to add your app specific settings in pyramid rest framework?

I am new to pyramid.
The Issue is I am not able to figure out how app specific settings (key value pairs) work in pyramid.
This is what I have done after various google searches and other stackoverflow answers:
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
if '__file__' in global_config:
settings.update(
load_sensitive_settings(global_config['__file__'], global_config))
config = Configurator(settings=settings)
config.include('pyramid_chameleon')
# config.add_static_view('static', 'static', cache_max_age=3600)
# config.add_route('home', '/')
config.add_route(
'tags',
'/tags', request_method='POST', accept='application/json', )
config.scan()
return config.make_wsgi_app()
def load_sensitive_settings(configurationPath, defaultByKey):
'Load sensitive settings from hidden configuration file'
# configFolder, configName = os.path.split(configurationPath)
# sensitivePath = os.path.join(configFolder, '.' + configName)
sensitivePath = configurationPath
settings = {}
configParser = ConfigParser.ConfigParser(defaultByKey)
if not configParser.read(sensitivePath):
log.warn('Could not open %s' % sensitivePath)
return settings
settings.update(configParser.items('custom'))
return settings
I have a file where I try to fetch settings like this:
from pyramid.threadlocal import get_current_registry
settings = get_current_registry().settings
value = settings['my_key']
But I always get settings object as None.
This is how I am defining my custom settings in development.ini
[custom]
my_key = ''
This is how I start my server in develpoment
pserve development.ini
I have read that request.settings can give me settings, But that approach is not feasible for me as my key contains the name of a file which is 1.5GBs and it has to be present in memory all the time. It takes around 5 minutes to load that file in server, hence cannot load the file on demand.
Please advice.
Thanks a lot for all the help in advance.
Update:
Thanks to all the answers provided, I finally made it work.
This is how my main function looks like:
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(settings=settings)
config.include('pyramid_chameleon')
if '__file__' in global_config:
init_config(global_config['__file__'])
And I made a config file, this is how my config file looks like:
import ConfigParser
settings = dict()
def init_config(filename):
config = ConfigParser.ConfigParser()
config.read(filename)
settings_dict = config.items('custom')
settings.update(settings_dict)
Now wherever I want settings, I just do:
from projectname.config import settings
settings.get('my_key')
And I put my app specific settings (development/production.py) like this
[custom]
my_key = value
Regards
HM
Easiest way is putting your settings to the app main section with dot separated names. Example:
[app:main]
websauna.site_name = Trees
websauna.site_tag_line = Enjoy
websauna.site_url = http://localhost:6543
websauna.site_author = Trees team
Then you can do:
my_settings_value = request.registry.settings.get("websauna.site_name", "Default value)
WSGI pipeline does not bring you settings from other sections and you need to reparse the INI file with ConfigParser if you want to access the other sections (as far as I know).
If you need to load a lot of data during development time just store a filename in settings and load the file when you need to access the data, so that you don't slow the web server startup.
Here is my working solution:
config.ini
[APP.CONFIG]
url = http://....
[SMTP.CONFIG]
smtp.server = ...
smtp.port = 25
smtp.login = ...
smtp.password = ...
smtp.from = ...
[DB.CONFIG]
db.database=...
db.host=...
db.port=..
db.user=...
db.password=...
config.py
import configparser
config = configparser.ConfigParser()
config._interpolation = configparser.ExtendedInterpolation()
config.read(encoding='utf-8', filenames=['path to file/config.ini'])
smtp = config['SMTP.CONFIG']
db = config['DB.CONFIG']
mail = config['APP.CONFIG']
And how i use it in APP
from config import db
host = db['db.host']
If, like me, you are using PasteDeploy with Pyramid, the Pyramid docs here explain how you can use a [DEFAULT] section in your .ini configuration file to hold your custom parameters.
You might also benefit from reading the documentation on .ini files, since it gives some snippets which make it all much clearer.

Python/Django: Get AttributeError when rendering html to pdf using xhtml2pdf

Here you can see the errors I get.
I am trying to render a pdf from a html template using Django.
This worked pretty well locally when I was fiddling about with it.
Managed to get a .pdf file from it.
Now I'm stuck with this error claiming that the uri has no attribute encode.
Has anyone had a similar error while using this python package? If so, how did you manage to fix it?
from __future__ import print_function
import easy_pdf
from deliveries.models import Delivery
from django.conf import settings
from easy_pdf.views import PDFTemplateView
import os
import sys
from django.core.wsgi import get_wsgi_application
from scripts import messages as msg
basename = os.path.splitext(os.path.basename(__file__))[0]
def run(*args, **kwargs):
if len(args) < 1 or args[0] is None:
print(msg.GeneralMessages.ERROR_DELIVERY_MISSING, file=sys.stderr)
sys.exit(1)
if not str(args[0]).isdigit():
logger.error(msg.GeneralMessages.ERROR_DELIVERY_INVALID)
sys.exit(1)
delivery = Delivery.get(args[0])
application = get_wsgi_application()
context = ''
view = ''
encoding = 'utf-8'
print(context)
view = easy_pdf.rendering.render_to_pdf('report.html', context)
if view:
f = open('report.pdf', 'wb')
f.write(view)
f.close()
print ('Report has been exported as .pdf')
if not settings.configured:
settings.configure(
DEBUG=True,
TIMEZONE="UTC",
INSTALLED_APPS=["easy_pdf", "django.contrib.staticfiles"],
TEMPLATE_DIRS=["../../deliveries/templates"],
ROOT_URLCONF=basename,
WSGI_APPLICATION="{}.application".format(basename),
STATIC_URL='/deliveries/static'
)

Binding Flask AutoIndex to a different URL other than http://localhost?

I am new to Flask and want to use the following basic example (using Flask-AutoIndex) in order to list files and folders in a directory:
import os.path
from flask import Flask
from flask.ext.autoindex import AutoIndex
app = Flask(__name__)
AutoIndex(app, browse_root=os.path.curdir)
if __name__ == '__main__':
app.run()
The example works fine for me,
http://localhost/folder1/folder2
is listing the files and folders in folder2.
The problem I have is that these urls are already bound to some other functions for other purposes, and this makes AutoIndex not working properly (URL conflicts)
Is it possible to bind AutoIndex to a different URL that has an extra word "list" in it? something like:
http://localhost/list/folder1/folder2/
http://localhost/list/folder1/folder2/folder3/
This works for me:
files_index = AutoIndex(app, os.path.curdir + '/app/files', add_url_rules=False)
# Custom indexing
#app.route('/files')
#app.route('/files/<path:path>')
def autoindex(path='.'):
return files_index.render_autoindex(path)
From https://github.com/sublee/flask-autoindex/issues/16
I can't test it, but AutoIndex includes an AutoIndexBlueprint, so I wonder if you could use that to tuck it away:
# bp.py
from flask import Blueprint
from flask.ext.autoindex import AutoIndexBlueprint
auto_bp = Blueprint('auto_bp', __name__)
AutoIndexBlueprint(auto_bp, browse_root='/tmp')
Then register it on your app:
from bp import auto_bp
app.register_blueprint(auto_bp, url_prefix='/list')

Categories