Python Paste
Install
easy_install paste easy_install pastescript
Create project
- This will create your project. We use paster create command to do that.
paster create myapp
- After you answer all question your folder structure should look like this:
- tree myapp
myapp |-- __init__.py |-- myapp | |-- __init__.py | `-- __init__.pyc |-- myapp.egg-info | |-- PKG-INFO | |-- SOURCES.txt | |-- dependency_links.txt | |-- entry_points.txt | |-- not-zip-safe | |-- paster_plugins.txt | `-- top_level.txt |-- setup.cfg `-- setup.py
Register your project
- To allow system to see your project you need to register it, but before you do that you need to add an entry point, and create a class that it will execute.
- In setup.py change the following so it looks like this:
entry_points="""
[paste.paster_create_template]
myapp = myapp.myapp:FrameworkTemplate
""",
And inside myapp folder in init.py add this.
/myapp/myapp/__init__.py
from paste.script import templates
class FrameworkTemplate(templates.Template):
egg_plugins = ['myapp']
summary = 'Template for creating a basic Framework package'
required_templates = ['basic_package']
_template_dir = 'templates'
use_cheetah = True
- Add templates folder to: /myapp/myapp/templates/
cd myapp/myapp mkdir templates cd templates mkdir +package+
- You can add all the files under '+package+'
- Now from the /myapp folder run this command. It will register you project with python environment.
python setup.py develop
- See if you can find it:
paster create --list-templates
- You can execute your app via:
paster create -t myapp or later paster create -t myapp SomeCommand
- You are done with initial setup. Now its time to build your app.
Templates
Template Structure
In class we added _template_dir = 'templates'.
- That folder will be our new project root folder.
- We add templates, +package+, and testfile.py_tmpl.
+package+ - Any directories with +package+ in their name will have that portion replaced by a simplified package name.
+egg+ - Any directories with +egg+ in their name will have that portion replaced by the name of the egg directory
__tmpl - Any files with _tmpl at the end of their filenames are treated as templates and will have the certain variable in a file replaced automatically
myapp |-- __init__.py |-- __init__.pyc |-- myapp | |-- __init__.py | |-- __init__.pyc | `-- templates | |-- +package+ | | `-- testfile.py_tmpl | `-- __init__.py |-- myapp.egg-info | |-- PKG-INFO | |-- SOURCES.txt | |-- dependency_links.txt | |-- entry_points.txt | |-- not-zip-safe | |-- paster_plugins.txt | `-- top_level.txt |-- setup.cfg `-- setup.py
- When you "python setup.py develop" and run below command you should get the following structure.
paster create -t "myapp"
- Structure you get will look like this. Note the testfile.py:
maa4 |-- __init__.py |-- maa4 | |-- __init__.py | `-- testfile.py |-- maa4.egg-info | |-- PKG-INFO | |-- SOURCES.txt | |-- dependency_links.txt | |-- entry_points.txt | |-- not-zip-safe | |-- paster_plugins.txt | `-- top_level.txt |-- setup.cfg `-- setup.py
Template Variables
*Based on the http://pythonpaste.org/script/developer.html#templates we add few more options to our init file. *required_templates = ['basic_package'] means that basic_package template will be run before ours. *You specify a list of variables.
from paste.script import templates
vars = [
templates.var('version', 'Version (like 0.1)'),
templates.var('description', 'One-line description of the package'),
templates.var('long_description', 'Multi-line description (in reST)'),
templates.var('keywords', 'Space-separated keywords/tags'),
templates.var('author', 'Author name'),
templates.var('author_email', 'Author email'),
templates.var('url', 'URL of homepage'),
templates.var('license_name', 'License name'),
templates.var('zip_safe', 'True/False: if the package can be distributed as a .zip file',
default=False),
]
- And then add them to your class.
class FrameworkTemplate(templates.Template):
egg_plugins = ['MyApp']
summary = 'Template for creating a basic Framework package'
required_templates = ['basic_package']
_template_dir = 'template'
use_cheetah = True
vars=vars
- The vars arguments then can be set inside of a Cheetah template variables in the files which end with _tmpl.
- You can access any of the vars we listed above by syntax like this:
${package}
${author}
- So in your myfile.py_tmpl you could do something like:
#This file was created by ${author}.
from ${package}.myfiles import myprogramfile
Customize the template
- If you want to customize the questions that are being asked of user, with something specific.
Example: You want to ask a question in a loop until done is entered, you have two options.
You can use a pre function which gets called after normal vars have been asked, or post function which gets asked after everything is setup.
- Add a pre function right after your template definition.
class FrameworkTemplate(templates.Template):
egg_plugins = ['MyApp']
summary = 'Template for creating a basic Framework package'
required_templates = ['basic_package']
_template_dir = 'template'
use_cheetah = True
vars=vars
#Customizing question
from paste.script import command
def pre(self, command, output_dir, vars):
vars['myquestion']=command.challenge('Please Enter A Name:','done',True)
- Or to make it a actual loop:
from paste.script import command
def pre(self, command, output_dir, vars):
vars['myquestion']=[]
while 1:
question=command.challenge('Please Enter A Name:','done',True)
if question=='done':
break
#Do what every you want with the new response below.
vars['myquestion'].append(question)
- Additional Examples:
http://pylonshq.com/project/pylonshq/browser/pylons/util.py#L148
http://lucasmanual.com/docs/paste-epydoc/paste.script.command-pysrc.html#Command.challenge
Cheetah and custom Templates
- Normally paste will copy everything from under template folder and +package+ folder, fill it and paste it into you package.
But if you want to do if somecondition: create this file then you have 2 options. You can create an if statement inside of your somefile.py_tmpl
#Please refer to cheetah docs for exact syntax #if not 'txt' in vars['myurl'] # exec raise SkipTemplate
- Or you need to take control of creating files using ensure file.
- Here is how you do it. In post function you add code to fill in the template and pass it to ensure file.
- You need to tell cheetah where is the source file, and pass the variables that you will be filling in.
from Cheetah.Template import Template #Provide source filename of where the file is: source_filename = os.path.join(os.path.dirname(__file__), 'somefolder/somefile.py_tmpl') #Fill in the template file content = Template(file=source_filename, searchList=[vars]) #Create a file, by passing the filename and content command.ensure_file(os.path.join(vars['package'], filename), str(content))
Create Automatic rst Syntax
- You could add to your template a file called readme.rst . Inside of it you can add the following code that will generate this:
myoproject documentation ========================
- But since we can not know how long will the description be we can use this code to make our "==="
#repeat $len($project) + 14 =#slurp #end repeat $project documentation
Commands
If you are not creating a new package. If you want to modify existing package you need to use commands portion of PythonPaste.
Create a command
The simples commands looks just like this. Take from original documentation
from paste.script import command
class MyCommand(command.Command):
max_args = 1
min_args = 1
usage = "NAME"
summary = "Say hello!"
group_name = "My Package Name"
parser = command.Command.standard_parser(verbose=True)
parser.add_option('--goodbye',
action='store_true',
dest='goodbye',
help="Say 'Goodbye' instead")
def command(self):
name = self.args[0]
if self.verbose:
print "Got name: %r" % name
if self.options.goodbye:
print "Goodbye", name
else:
print "Hello", name
To see more advanced command read the following create distro
Attributes
- You set number of attributes with:
min_args = 0 max_args = 1 or min_args = 1 max_args = 1
The top one doesn't required any arguments. Example paster mycommand vs paster mycommand --dosomething
Group Name
- Group name just tells what type of command is it.
- Run
paster
- You should see groups like this:
Commands: create Create the file layout for a Python distribution grep Search project for symbol help Display help make-config Install a package and create a fresh config file/directory points Show information about entry points post Run a request for the described application request Run a request for the described application serve Serve the described application setup-app Setup an application, given a config file TurboGears2: crud Generate CRUD interface based on model migrate Sqlalchemy migration quickstart Create a new TurboGears 2 project. tginfo Show TurboGears 2 related projects and their versions pylons: controller Create a Controller and accompanying functional test restcontroller Create a REST Controller and accompanying functional test shell Open an interactive shell with the Pylons app loaded
- To make your package part of turbogears2 group add below:
group_name = 'TurboGears2'
add option
You add options like paster mycommand --output-dir ./path/to/ or paster mycommand -o ./path/to/ by doing:
parser.add_option('-o', '--output-dir',
dest='output_dir',
metavar='DIR',
default='.',
help="Write put the directory into DIR (default current directory)")
- If you have above command you can add this line to your command, and it will tell you where should you output your results:
output_dir = os.path.join(self.options.output_dir, 'somefolder')
def command(self)
- This function gets executed and content should have the code that will do what you need it to do.
There are few functions that paste.script already comes with, so use these in your command() function.
- Take advantage of:
paste.script.checkperms paste.script.command paste.script.copydir paste.script.filemaker paste.script.templates paste.script.testapp paste.script.util.secret
parse PKG-INFO
import pkg_resources
for ep in pkg_resources.iter_entry_points(
'paste.app_factory','main'):
print ep
dir(ep)
from paste.script import pluginlib
import os, sys
egg_info_dir = pluginlib.find_egg_info_dir(os.getcwd())
plugins= os.path.splitext(os.path.basename(egg_info_dir))[0]
print os.path.splitext(os.path.basename(egg_info_dir))[0]
dist= pluginlib.get_distro(plugins)
if dist.has_metadata('PKG-INFO'):
data=dist.get_metadata('PKG-INFO')
for add_info in pluginlib.parse_lines(data):
print add_info
Entry Point
paste.paster_command
- You can use this command inside you project, or if you want to use it outside of your project that project needs to have you project name inside paster_plugins.txt .
[paste.paster_command]
mycommand = mypackage.mycommand:MyCommand
- The global command is visible to any project, but if you want a "paster_command" from you application be displayed inside another project you needs to have your application name be added to paster_plugin.txt
- So in my case if I want my new command to be displayed as a local command inside a tg2 application I need to do:
echo "mygreatnewapplication" >> ./sometg2app.egg-info/paster_plugins.txt or echo "modwsgideploy" >> ./myapp.egg-info/paster_plugins.txt
- If Then inside of myapp I will be able to see the commands for mygreatenewapplication by running:
paster
paste.global_paster_command
- This command is visible to a global paste command. You can run this by doing 'paster mycommand'
[paste.global_paster_command]
myglobal = mypackage.myglobal:MyGlobalCommand
Installing package
setup.py
When setting up the setup.py you should read this: Python distribution of packages.
- It explains in details the difference between packages, package_date and package_dir.
The next read is an article on IBM site Python and Setuptools
Subfolders and plugins tutorial subfolders and plugins
Package and upload to PYPI
- To package your new application use:
python setup.py sdist or python setup.py sdist --formats=gztar,zip
- You can also upload it to PYPI
python setup.py register python setup.py sdist upload
MANIGEST.in
Sometimes the sdist command doesn't include all the files that don't end with txt or py. To fix this you need to add a MANIFEST.in to the directory. Inside of manifest add the following. Please specify which folder:
recursive-include somefolder *.txt *.py *_tmpl *.wsgi or recursive-include templates *.txt *.py *_tmpl *.wsgi or recursive-include somefolder2 *.txt *.py *_tmpl *.wsgi
setuptools required
- If the system you are installing on does not have the setuptools you can add the following to your code.
- Add to the beginning of setup.py:
#Gets setuptools
try:
from setuptools import setup, find_packages
except ImportError:
from ez_setup import use_setuptools
use_setuptools()
from setuptools import setup, find_packages
- Add ez_setup.py to your folder:
wget http://peak.telecommunity.com/dist/ez_setup.py
Troubleshooting entry points
- One of the primary reasons your entry point doesn't show up as available paster command or template is because it generates an error.
If for some reason the template or the command doesn't appear in paster or paster create --list-tempaltes you can troubleshoot to see if that is the case.
from pkg_resources import *
for i in iter_entry_points("paste.paster_command"):
print i
- If it doesn't show you template or command then you have setup entry point incorrectly.
- If it does show your template/command move on to the next step:
- If your entry point looks like this:
[paste.paster_command]
modwsgi_deploy = modwsgideploy.commands:ModwsgiCommand
- Try loading it by doing:
load_entry_point("modwsgideploy", "paste.paster_command", "modwsgi_deploy")
*Try running it:
load_entry_point("modwsgideploy", "paste.paster_command", "modwsgi_deploy")()
or
load_entry_point("modwsgideploy", "paste.paster_command", "modwsgi_deploy")('someoption').command()
- Random Notes. Please disregard below section.
(10:28:29 AM) luca1: I'm trying to implement a paster create -t "mytemplate" , as one of the questions I need to as after license, project owner etc is "enter filename:" ; I need it to be in a loop. so enter filename 1: , enter filename 2, enter filename 3 until "done" is passed... How do I do that.
(10:28:36 AM) sanjiv_ [n=sanjiv-c@59.180.154.228] entered the room.
(10:29:16 AM) sanjiv left the room (quit: Nick collision from services.).
(10:29:18 AM) luca1: I've been told I can do: while f != "done":
(10:29:18 AM) luca1: files.append(f)
(10:29:18 AM) luca1: f = self.ask("Enter file or 'done':") something like thatwhile 1:
(10:29:18 AM) luca1: f = self.ask("Enter file or 'done':")
(10:29:18 AM) luca1: if f == 'done':
(10:29:18 AM) luca1: break
(10:29:18 AM) luca1: files.append(f)
(10:29:18 AM) luca1: But where do I paste this code?
(10:29:27 AM) kumar: btw, for anyones interest, luca1 is working on a manual for creating custom paste commands here: http://lucasmanual.com/mywiki/Paste
(10:29:38 AM) sanjiv_ is now known as sanjiv
(10:29:58 AM) luca1: http://lucasmanual.com/mywiki/Paste#head-8ef91046e3015539e95435f0c1bcc434d7f41f78
(10:30:26 AM) luca1: Above is how I overload the template questions... But How do I create a loop with my list of files?
(10:30:40 AM) anilm [n=anilm@64.90.184.79.static.nyinternet.net] entered the room.
(10:33:07 AM) kumar: luca1 the piece you are missing so far is a custom command -- this should get you started: http://pythonpaste.org/script/developer.html
(10:34:09 AM) luca1: So you are saying I should do:
(10:34:09 AM) luca1: setup(... entry_points=""" [paste.paster_command] mycommand = mypackage.mycommand:MyCommand
(10:34:52 AM) luca1: then...
(10:34:52 AM) luca1: from paste.script import command class MyCommand(command.Command): max_args = 1 min_args = 1
(10:35:22 AM) luca1: and do my asking in here?
(10:35:22 AM) luca1: def command(self): name = self.args[0] if self.verbose:
(10:35:27 AM) luca1: f = self.ask("Enter file or 'done':") something like thatwhile 1:
(10:35:36 AM) kumar: yes
(10:36:14 AM) luca1: how do I run the command now? paster create -t "myapp" --enterfiles ?
(10:36:16 AM) kumar: self.challenge("enter file") not ask()
(10:37:11 AM) luca1: what is the difference between ask and challenge?
(10:37:27 AM) kumar: I think there is no ask, sorry, just my bad memory
(10:37:42 AM) luca1: I see.
(10:38:31 AM) luca1: So is above how I run my command? paster create -t "myapp" --enterfiles
(10:39:07 AM) kumar: have a read through those docs again
(10:39:15 AM) kumar: paster mycommand
(10:39:17 AM) kumar: is how you run it
(10:39:48 AM) kumar: but plugging into paster create is a little different. I'm not sure where the docs are for that
(10:42:25 AM) luca1: so... I have to install the template first, then I can use my command? How do I get to "paster mycommand" part?
(10:44:43 AM) kumar: the same way you got your templates to show up in the list, from the manual you are working on
(10:44:47 AM) luca1: What my goal is to use "paster create -t"mytemplate" which would create a project file that somebody would be working on. As part of that project creation I need to have a list of files entered.
(10:44:54 AM) kumar: except this time you need the new entry point mycommand = ...
(10:45:02 AM) kumar: then run python setup.py develop again
(10:45:12 AM) kumar: and it will appear as paster mycommand
(10:45:24 AM) luca1: I see...
(10:46:06 AM) kumar: if you want to customize paster create that is a little different
(10:46:08 AM) luca1: So I guess do I use paster mycommand to create the project folder, or paster create ....?
(10:47:00 AM) whit [n=whit@adsl-156-6-60.bna.bellsouth.net] entered the room.
(10:47:28 AM) luca1: If the only way to get my project folder the way I need it then I guess I have to do just that.? Unless I can do it from my command?
(10:48:11 AM) kumar: if you want to customize paster create then you'll have to go back to working in the Template class
(10:48:28 AM) luca1: ok
(10:50:06 AM) kumar: read the code in paste.script.templates to learn more about that, or read the code in pylons.util.PylonsTemplate to see an example of how paster create -t "pylons" works
(10:50:20 AM) luca1: So from above I can use paste to either create me project folders or add commands which will do xyz..? Right...
(10:50:20 AM) luca1: So we go back to original question, how do I customize paster create template/ template class to ask a question in a loop until I get "done"
(10:51:28 AM) kumar: the answer is put self.challenger(...) in the pre() method of your Template class, I think
(10:51:58 AM) kumar: you'll have to experiment with it and you will want to read the code in paste.script.templates.Template since I don't see any docs on it
(10:52:33 AM) luca1: Where was the second example? http://pylonshq.com/project/pylonshq/browser/pylons/templates/default_project
(10:53:05 AM) kumar: here: http://pylonshq.com/project/pylonshq/browser/pylons/util.py#L148
(10:54:07 AM) luca1: From here http://wiki.pylonshq.com/display/pylonscookbook/Creating+Templates+For+The+paster+create+Command I will use :
(10:54:07 AM) luca1: class ArtProjectTemplate(Template): _template_dir = 'templates/default_project' summary = 'Art project template' vars = vars
(10:54:38 AM) luca1: I'll try to put the self.challenge after vars and see if that will work.
(10:57:51 AM) luca1: From the util..py.. You are correct def pre seems to be a way to go.. I wonder if there is a post..? I would like to ask for files after everything else is filled.
(10:57:52 AM) luca1: ?
(10:58:05 AM) rhymes left the room (quit: "Ex-Chat").
(10:58:31 AM) kumar: yes, there is a post. read through paste.script.templates.Template -- that is the best way to understand what is available to you ;)
(11:02:06 AM) luca1: where would that be again? Somewhere here ? http://svn.pythonpaste.org/Paste/trunk/paste/
(11:10:53 AM) gldnspud [n=gldnspud@74-61-143-162.bel.clearwire-dns.net] entered the room.
(11:14:28 AM) luca1: ?
(11:46:42 AM) cmlenz left the room (quit: ).
(11:49:09 AM) luca1: /usr/lib/python2.4/site-packages/paste/script/templates.py it is..
(11:50:07 AM) luca1: Thanks for the Information Kumar! I will give it a try some time this week... Appreciate it! Thanks.
Link References
Intro to Eggs: http://ianbicking.org/docs/pycon2006/plugins.html
Intro to Paste: http://www.groovie.org/2005/10/04/python-paste-power
Intro to Paste and Setuptools: http://www.groovie.org/2005/09/29/setuptools-and-python-paste
Main doc for Template: http://pythonpaste.org/script/developer.html#templates
Create Paster project: http://www.percious.com/blog/?tag=python-setuptools-paster-paste-omnigraffle-agile
Entry Point: http://trac.turbogears.org/browser/projects/tg.devtools/trunk/setup.py#L35
Extending Templates: http://docs.turbogears.org/1.0/ExtendingQuickstart
Extending Templates: http://wiki.pylonshq.com/display/pylonscookbook/Creating%20Templates%20For%20The%20paster%20create%20Command
Extending Template with pre and post functions: http://trac.turbogears.org/browser/projects/tg.devtools/trunk/devtools/pastetemplate.py
Sample File http://svn.pythonpaste.org/Paste/Script/trunk/paste/script/create_distro.py
Simple plugin Overview: http://base-art.net/Articles/64/
Sample Commands commends.py file: http://pylonshq.com/hg/pylons-dev/file/eca8fb3becdf/pylons/commands.py
Create distor command sample file: http://pythonpaste.org/script/paste/script/create_distro.py.html#12