Differences between revisions 56 and 57
Revision 56 as of 2009-12-02 23:05:13
Size: 24507
Comment:
Revision 57 as of 2017-06-24 05:29:37
Size: 24705
Comment:
Deletions are marked like this. Additions are marked like this.
Line 417: Line 417:
#New format: see [[https://packaging.python.org/tutorials/distributing-packages/#wheels Python distribution of packages]]
{{{
python install wheel
python setup.py bdist_wheel --universal
}}}

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)

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

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

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)

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

Package and upload to PYPI

  • To package your new application use:

python setup.py sdist
or
python setup.py sdist --formats=gztar,zip

#New format: see https://packaging.python.org/tutorials/distributing-packages/#wheels Python distribution of packages

python install wheel
python setup.py bdist_wheel --universal
  • 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

check your long_description

  • To make your life easier, you can test for reST compliancy like this:

$ python setup.py --long-description | rst2html > /dev/null

It will displays warnings and errors.

  • In the future you will be able to do:

python setup.py check --restructuredtext

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

MyWiki: PythonPaste (last edited 2017-06-24 05:29:37 by LukaszSzybalski)