pax_global_header00006660000000000000000000000064147442144760014527gustar00rootroot0000000000000052 comment=9a44ce91930108ca1230433e6d6f9c73892c8414 pastescript-3.7.0/000077500000000000000000000000001474421447600140775ustar00rootroot00000000000000pastescript-3.7.0/.github/000077500000000000000000000000001474421447600154375ustar00rootroot00000000000000pastescript-3.7.0/.github/workflows/000077500000000000000000000000001474421447600174745ustar00rootroot00000000000000pastescript-3.7.0/.github/workflows/tests.yaml000066400000000000000000000015631474421447600215270ustar00rootroot00000000000000name: tests on: - push - pull_request - workflow_dispatch jobs: build: runs-on: ubuntu-latest strategy: matrix: include: - python: 3.8 toxenv: py38 - python: 3.9 toxenv: py39 - python: "3.10" toxenv: py310 - python: "3.11" toxenv: py311 - python: "3.12" toxenv: py312 - python: "3.13" toxenv: py313 name: ${{ matrix.toxenv }} on Python ${{ matrix.python }} steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - run: pip install tox - run: tox env: TOXENV: ${{ matrix.toxenv }} pastescript-3.7.0/.gitignore000066400000000000000000000000551474421447600160670ustar00rootroot00000000000000*.pyc .tox .coverage *.egg-info docs/_build/ pastescript-3.7.0/.readthedocs.yaml000066400000000000000000000011011474421447600173170ustar00rootroot00000000000000# .readthedocs.yaml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-22.04 tools: python: "3.11" # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py # We recommend specifying your dependencies to enable reproducible builds: # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html # python: # install: # - requirements: docs/requirements.txt pastescript-3.7.0/AUTHORS000066400000000000000000000010301474421447600151410ustar00rootroot00000000000000PasteScript authors =================== * Atsushi Odagiri aka aodag * bbangert * Chris McDonough * Clay Gerrard * FGtatsuro * flynsqrl * Gary van der Merwe * Ian Bicking * Jaap Roes * kevin * Marc Abramowitz * Mike Gilbert * pjenvey * rflosi * Richard Mitchell * thecrypto * thejimmyg * Victor Stinner pastescript-3.7.0/MANIFEST.in000066400000000000000000000006231474421447600156360ustar00rootroot00000000000000recursive-include docs *.txt include docs/conf.py include docs/*.ini include regen-docs include scripts/paster include tests/sample_templates/*tmpl include tox.ini recursive-exclude docs/_build/_sources * recursive-include docs/_build *.html recursive-include tests *.txt *.py PKG-INFO recursive-include paste *.js *.jpg recursive-include paste/script/paster-templates/basic_package setup.* *.py *_tmpl pastescript-3.7.0/Makefile000066400000000000000000000016141474421447600155410ustar00rootroot00000000000000# simple Makefile for some common tasks .PHONY: clean test dist release pypi tagv pastescript-version := $(shell python setup.py --version) clean: find . -name "*.pyc" |xargs rm || true rm -r dist || true rm -r build || true rm -rf .tox || true rm -r cover .coverage || true rm -r .eggs || true rm -r pastescript.egg-info || true tagv: git tag -s -m ${pastescript-version} ${pastescript-version} git push origin master --tags cleanagain: find . -name "*.pyc" |xargs rm || true rm -r dist || true rm -r build || true rm -r .tox || true rm -r cover .coverage || true rm -r .eggs || true rm -r pastescript.egg-info || true test: tox --skip-missing-interpreters dist: test python3 setup.py sdist bdist_wheel release: clean test cleanagain tagv pypi gh pypi: python3 setup.py sdist bdist_wheel twine upload dist/* gh: gh release create ${pastescript-version} --generate-notes dist/* pastescript-3.7.0/README.rst000066400000000000000000000023631474421447600155720ustar00rootroot00000000000000PasteScript is a pluggable command-line tool. **Note**: Paste Script is being maintained on life support. That means that critical bugs will be fixed, and support for new versions of Python will be handled, but other than that new features are not being considered. **With version 3.6.0 pastescript development moves to the pasteorg GitHub organization and will be going deeper into maintenance mode unless more active maintainers step forward to take over. "Deeper" in this case means that releases will be much less frequent and patches will only be accepted for security issues or major problems. Current consumers of pastescript should prepare to migrate away to more modern solutions.** It includes some built-in features; * Create file layouts for packages. For instance, ``paster create --template=basic_package MyPackage`` will create a `setuptools `_-ready file layout. * Serving up web applications, with configuration based on `paste.deploy `_. The latest version is available in a `GitHub repository `_. For the latest changes see the `news file `_. pastescript-3.7.0/docs/000077500000000000000000000000001474421447600150275ustar00rootroot00000000000000pastescript-3.7.0/docs/conf.py000066400000000000000000000074731474421447600163410ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Paste documentation build configuration file, created by # sphinx-quickstart on Tue Apr 22 22:08:49 2008. # # This file is execfile()d with the current directory set to its containing dir. # # The contents of this file are pickled, so don't put values in the namespace # that aren't pickleable (module imports are okay, they're removed automatically). # # All configuration values have a default value; values that are commented out # serve to show the default value. # If your extensions are in another directory, add it here. #import sys #sys.path.append('some/directory') # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.txt' # The master toctree document. master_doc = 'index' # General substitutions. project = 'Paste Script' copyright = '2011, Ian Bicking' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. # # The short X.Y version. version = '' # The full version, including alpha/beta/rc tags. release = '' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = ['include/contact.txt', 'include/reference_header.txt'] # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # Options for HTML output # ----------------------- html_theme = 'nature' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Content template for the index page. #html_index = '' # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If true, the reST sources are included in the HTML build as _sources/. #html_copy_source = True # Output file base name for HTML help builder. htmlhelp_basename = 'PasteScriptdoc' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). #latex_documents = [] # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True pastescript-3.7.0/docs/developer.txt000066400000000000000000000167771474421447600175770ustar00rootroot00000000000000Paste Script: Development ========================= :author: Ian Bicking :revision: $Rev$ :date: $LastChangedDate$ .. contents:: Introduction ------------ This document is an introduction to how you can extend ``paster`` and Paste Script for your system -- be it a framework, server setup, or whatever else you want to do. What Paste Script Can Do ------------------------ ``paster`` is a two-level command, where the second level (e.g., ``paster help``, ``paster create``, etc) is pluggable. Commands are attached to `Python Eggs `_, i.e., to the package you distribute and someone installs. The commands are identified using `entry points `_. To make your command available do something like this in your ``setup.py`` file:: from setuptools import setup setup(... entry_points=""" [paste.paster_command] mycommand = mypackage.mycommand:MyCommand [paste.global_paster_command] myglobal = mypackage.myglobal:MyGlobalCommand """) This means that ``paster mycommand`` will run the ``MyCommand`` command located in the ``mypackage.mycommand`` module. Similarly with ``paster myglobal``. The distinction between these two entry points is that the first will only be usable when ``paster`` is run inside a project that is identified as using your project, while the second will be globally available as a command as soon as your package is installed. How's the Local Thing Work? --------------------------- So if you have a local command, how does it get enabled? If the person is running ``paster`` inside their project directory, ``paster`` will look in ``Project_Name.egg-info/paster_plugins.txt`` which is a list of project names (the name of your package) whose commands should be made available. This is for frameworks, so frameworks can add commands to ``paster`` that only apply to projects that use that framework. What Do Commands Look Like? --------------------------- The command objects (like ``MyCommand``) are subclasses of ``paste.script.command.Command``. You can look at that class to get an idea, but a basic outline looks like this:: 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 ``max_args`` and ``min_args`` are used to give error messages. You can also raise ``command.BadCommand(msg)`` if the arguments are incorrect in some way. (Use ``None`` here to give no restriction) The ``usage`` variable means ``paster mycommand -h`` will give a usage of ``paster mycommand [options] NAME``. ``summary`` is used with ``paster help`` (describing your command in a short form). ``group_name`` is used to group commands together for ``paste help`` under that title. The ``parser`` object is an `optparse ` ``OptionParser`` object. ``Command.standard_parser`` is a class method that creates normal options, and enables options based on these keyword (boolean) arguments: ``verbose``, ``interactive``, ``no_interactive`` (if interactive is the default), ``simulate``, ``quiet`` (undoes verbose), and ``overwrite``. You can create the parser however you want, but using ``standard_parser()`` encourages a consistent set of shared options across commands. When your command is run, ``.command()`` is called. As you can see, the options are in ``self.options`` and the positional arguments are in ``self.args``. Some options are turned into instance variables -- especially ``self.verbose`` and ``self.simulate`` (even if you haven't chosen to use those options, many methods expect to find some value there, which is why they are turned into instance variables). There are quite a few useful methods you can use in your command. See the `Command class `_ for a complete list. Some particulars: ``run_command(cmd, arg1, arg2, ..., cwd=os.getcwd(), capture_stderr=False)``: Runs the command, respecting verbosity and simulation. Will raise an error if the command doesn't exit with a 0 code. ``insert_into_file(filename, marker_name, text, indent=False)``: Inserts a line of text into the file, looking for a marker like ``-*- marker_name -*-`` (and inserting just after it). If ``indent=True``, then the line will be indented at the same level as the marker line. ``ensure_dir(dir, svn_add=True)``: Ensures that the directory exists. If ``svn_add`` is true and the parent directory has an ``.svn`` directory, add the new directory to Subversion. ``ensure_file(filename, content, svn_add=True)``: Ensure the file exists with the given content. Will ask the user before overwriting a file if ``--interactive`` has been given. Templates --------- The other pluggable part is "templates". These are used to create new projects. Paste Script includes one template itself: ``basic_package`` which creates a new setuptools package. To enable, add to ``setup.py``:: setup(... entry_points=""" [paste.paster_create_template] framework = framework.templates:FrameworkTemplate """) ``FrameworkTemplate`` should be a subclass of ``paste.script.templates.Template``. An easy way to do this is simply with:: from paste.script import templates class FrameworkTemplate(templates.Template): egg_plugins = ['Framework'] summary = 'Template for creating a basic Framework package' required_templates = ['basic_package'] _template_dir = 'template' use_cheetah = True ``egg_plugins`` will add ``Framework`` to ``paste_plugins.txt`` in the package. ``required_template`` means those template will be run before this one (so in this case you'll have a complete package ready, and you can just write your framework files to it). ``_template_dir`` is a module-relative directory to find your source files. The source files are just a directory of files that will be copied into place, potentially with variable substitutions. Three variables are expected: ``project`` is the project name (e.g., ``Project-Name``), ``package`` is the Python package in that project (e.g., ``projectname``) and ``egg`` is the project's egg name as generated by setuptools (e.g., ``Project_Name``). Users can add other variables by adding ``foo=bar`` arguments to ``paster create``. Filenames are substituted with ``+var_name+``, e.g., ``+package+`` is the package directory. If a file in the template directory ends in ``_tmpl`` then it will be substituted. If ``use_cheetah`` is true, then it's treated as a `Cheetah `_ template. Otherwise `string.Template `_ is used, though full expressions are allowed in ``${expr}`` instead of just variables. See the `templates module `_ for more. pastescript-3.7.0/docs/example_app.ini000077500000000000000000000010721474421447600200260ustar00rootroot00000000000000#!/usr/bin/env paster exe serve # This file serves the roles of being an rc script, you # can run it with "./example_wsgiutils_app.ini start" # plus start and restart. [exe] # Here we could set options for how this server should # start. These all correspond to command-line options: # user = username # group = groupname # daemon = true # pid_file = /path/to/pid # log_file = /path/to/log # reload = true # reload_interval = 10 # For this example we'll daemonize: daemon = true [server:main] use = egg:Paste#http port = 8080 [app:main] use = egg:PasteScript#test pastescript-3.7.0/docs/example_cgi_app.ini000077500000000000000000000001571474421447600206530ustar00rootroot00000000000000#!/usr/bin/env paster exe serve [server:main] use = egg:PasteScript#cgi [app:main] use = egg:PasteScript#test pastescript-3.7.0/docs/index.txt000066400000000000000000000156411474421447600167060ustar00rootroot00000000000000Paste Script ============ :author: Ian Bicking :revision: $Rev$ :date: $LastChangedDate$ Contents: .. toctree:: :maxdepth: 1 news developer license Warning ------- Paste Script is being maintained on life support. That means that critical bugs will be fixed, and support for new versions of Python will be handled, but other than that new features are not being considered. Development has moved to `GitHub `_. Introduction ------------ If you are developer, see the `Developer Documentation `_; this will tell you how to add commands and templates to ``paster``. For a list of updates see the `news file `_. Paste Script is released under the `MIT license `_. Status ------ Paste Script has passed version 1.0. Paste Script is in maintenance mode. Bugs will be fixed, new features are not being considered. ``paster create`` ----------------- This creates the skeleton for new projects. Many different kinds of projects have created skeletons for their projects (Pylons, TurboGears, ZopeSkel, and others). For a tutorial for making new skeletons, see `this tutorial from Lucas Szybalski `_. It also discusses creating new subcommands for paster. ``paster serve`` ---------------- The one useful command you may want to know about for ``paster`` is ``paster serve``. This serves an application described in a `Paste Deploy `_ configuration file. Configuration ------------- A quickstart (and example), if not complete explanation:: [app:main] use = egg:PasteEnabledPackage option1 = foo option2 = bar [server:main] use = egg:PasteScript#wsgiutils host = 127.0.0.1 port = 80 ``egg:PasteEnabledPackage`` refers to some package that has been prepared for use with paste.deploy, and options given to that package. If you are starting out with some framework, you'll have to reference some documentation for that framework to paste-deploy-ify your application (or read the paste.deploy documentation). In the same file is a server description. ``egg:PasteScript#wsgiutils`` is a server (named ``wsgiutils``) provided by this package, based on `WSGIUtils `_. And we pass various options particular to that server. Other packages can provide servers, but currently Paste Script includes glue for these: ``wsgiutils``: A `SimpleHTTPServer `_ based threaded HTTP server, using `WSGIUtils `_. ``flup_(scgi|fcgi|ajp)_(thread|fork)``: This set of servers supports `SCGI `_, `FastCGI `_ and `AJP `_ protocols, for connection an external web server (like Apache) to your application. Both threaded and forking versions are available. This is based on `flup `_. There is the start of support for `twisted.web2 `_ in ``paste.script.twisted_web2_server``; patches welcome. Running the Server ------------------ ``paster serve --help`` gives useful output:: usage: /usr/local/bin/paster serve [options] CONFIG_FILE [start|stop|restart|status] Serve the described application If start/stop/restart is given, then it will start (normal operation), stop (--stop-daemon), or do both. You probably want ``--daemon`` as well for stopping. Options: -h, --help show this help message and exit -v, --verbose -q, --quiet -nNAME, --app-name=NAME Load the named application (default main) -sSERVER_TYPE, --server=SERVER_TYPE Use the named server. --server-name=SECTION_NAME Use the named server as defined in the configuration file (default: main) --daemon Run in daemon (background) mode --pid-file=FILENAME Save PID to file (default to paster.pid if running in daemon mode) --log-file=LOG_FILE Save output to the given log file (redirects stdout) --reload Use auto-restart file monitor --reload-interval=RELOAD_INTERVAL Seconds between checking files (low number can cause significant CPU usage) --status Show the status of the (presumably daemonized) server --user=USERNAME Set the user (usually only possible when run as root) --group=GROUP Set the group (usually only possible when run as root) --stop-daemon Stop a daemonized server (given a PID file, or default paster.pid file) Basically you give it a configuration file. If you don't do anything else, it'll serve the ``[app:main]`` application with the ``[server:main]`` server. You can pass in ``--server-name=foo`` to serve the ``[server:foo]`` section (or even ``--server-name=config:foo.ini`` to use a separate configuration file). Similarly you can use ``--app-name=foo`` to serve ``[app:foo]``. ``--daemon`` will run the server in the backgroup, ``--user`` and ``--group`` will set the user, as you might want to do from a start script (run as root). If you don't give a ``--pid-file`` it will write the pid to ``paster.pid`` (in the current directory). ``--stop-daemon`` will stop the daemon in ``paster.pid`` or whatever ``--pid-file`` you give. ``--log-file`` will redirect stdout and stderr to that file. ``--reload`` will start the reload monitor, and restart the server whenever a file is edited. This can be a little expensive, but is very useful during development. #! Scripts ---------- On Posix (Linux, Unix, etc) systems you can turn your configuration files into executable scripts. First make the file executable (``chmod +x config_file.ini``). The you should add a line like this to the top of the file:: #!/usr/bin/env paster You can include a command and command-line options in an ``[exe]`` section, like:: [exe] command = serve daemon = true user = nobody group = nobody (use ``true`` and ``false`` for options that don't take an argument). If you use ``daemon = true`` then you'll be able to use the script as an rc script, so you can do:: $ sudo ./config_file.ini start $ sudo ./config_file.ini restart and so forth. Note that this is a little wonky still on some platforms and shells (notably it doesn't work under `csh `_). If you get an error about "Command config_file.ini not known" then this probably won't work for you. In the future an additional script to ``paster`` will be added just for this purpose. pastescript-3.7.0/docs/license.txt000066400000000000000000000020651474421447600172150ustar00rootroot00000000000000Copyright (c) 2006-2007 Ian Bicking and Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pastescript-3.7.0/docs/modules/000077500000000000000000000000001474421447600164775ustar00rootroot00000000000000pastescript-3.7.0/docs/modules/checkperms.txt000066400000000000000000000011251474421447600213630ustar00rootroot00000000000000:mod:`paste.script.checkperms` -- functions to check and diff file permissions ============================================================================== .. automodule:: paste.script.checkperms .. contents:: Module Contents --------------- Permissions ~~~~~~~~~~~ .. autofunction:: read_perm_spec .. autofunction:: mode_diff .. autofunction:: calc_mode_diff .. autofunction:: calc_set_mode .. autofunction:: set_mode Ownership ~~~~~~~~~ .. autofunction:: calc_ownership_spec .. autofunction:: ownership_diff .. autofunction:: set_ownership Models ~~~~~~ .. autoclass:: PermissionSpec pastescript-3.7.0/docs/modules/cherrypy_server.txt000066400000000000000000000005111474421447600224700ustar00rootroot00000000000000:mod:`paste.script.cherrypy_server` -- CherryPy WSGI server =========================================================== This is a slightly repackaged version of the WSGI HTTP server in `CherryPy `_. .. automodule:: paste.script.wsgiserver Module Contents --------------- .. autofunction:: cpwsgi_server pastescript-3.7.0/docs/modules/command.txt000066400000000000000000000003701474421447600206560ustar00rootroot00000000000000:mod:`paste.script.command` -- framework for paster commands ============================================================ .. automodule:: paste.script.command Module Contents --------------- .. autoexception:: BadCommand .. autoclass:: Command pastescript-3.7.0/docs/modules/copydir.txt000066400000000000000000000004721474421447600207140ustar00rootroot00000000000000:mod:`paste.script.copydir` -- recursive, interactive copy ========================================================== .. automodule:: paste.script.copydir Module Contents --------------- .. autofunction:: copy_dir .. autoexception:: SkipTemplate .. autofunction:: query_interactive .. autoclass:: LaxTemplate pastescript-3.7.0/docs/modules/filemaker.txt000066400000000000000000000003441474421447600212000ustar00rootroot00000000000000:mod:`paste.script.filemaker` -- interactive file writing helpers ================================================================= .. automodule:: paste.script.filemaker Module Contents --------------- .. autoclass:: FileOp pastescript-3.7.0/docs/modules/templates.txt000066400000000000000000000004401474421447600212340ustar00rootroot00000000000000:mod:`paste.script.templates` -- framework for paster create templates ====================================================================== .. automodule:: paste.script.templates Module Contents --------------- .. autoclass:: Template .. autoclass:: var .. autoclass:: BasicPackage pastescript-3.7.0/docs/modules/testapp.txt000066400000000000000000000003411474421447600207160ustar00rootroot00000000000000:mod:`paste.script.testapp` -- simple test WSGI application =========================================================== .. automodule:: paste.script.testapp Module Contents --------------- .. autoclass:: TestApplication pastescript-3.7.0/docs/modules/util.secret.txt000066400000000000000000000003401474421447600214760ustar00rootroot00000000000000:mod:`paste.script.util.secret` -- create random secrets ======================================================== .. automodule:: paste.script.util.secret Module Contents --------------- .. autofunction:: secret_string pastescript-3.7.0/docs/news.txt000066400000000000000000000371521474421447600165540ustar00rootroot00000000000000News: Paste Script ================== .. contents:: 3.7.0 (2025-01-22) ------------------ * Open, read and write files in binary mode in copydir.py. * Fix locking for modern Python. 3.6.0 (2024-04-27) ------------------ * Pastescript is moved to https://github.com/pasteorg/pastescript With version 3.6.0 pastescript development moves to the pasteorg GitHub organization and will be going deeper into maintenance mode unless more active maintainers step forward to take over. "Deeper" in this case means that releases will be much less frequent and patches will only be accepted for security issues or major problems. Current consumers of pastescript should prepare to migrate away to more modern solutions. 3.5.1 (2024-03-15) ------------------ * Be explicit about Python 3 being required. 3.5.0 (2024-03-14) ------------------ * Remove remains of Python 2 in code. Thanks to Alexandre Detiste (a-detiste) 3.4.0 (2024-01-22) ------------------ * Correct working with PasteDeploy >=3. Thanks brondsem. 3.3.0 (2023-01-03) ------------------ * Remove support for Python 2 in tests. It may still work outside tests. * Fix homepage link. Thanks to Guillaume Gauvrit (mardiros). * Stop using nose to run tests. * Run tests in GitHub actions instead of travis CI. 3.2.1 (2021-04-27) ------------------ * Require ``setuptools`` in ``install_requires``. Thanks to Tomáš Hrnčiar (hrnciar) * Fix tests to run again. 3.2.0 (2019-09-24) ------------------ * Use wsgiserver.WSGIServer instead of wsgiutils.wsgiServer.WSGIServer for Python 3 compatibility. 3.1.0 (2019-03-04) ----------------- * Remove dependency on ``unittest2``. 3.0.0 (2018-11-26) ------------------ * Moved to `GitHub `_. * Put into maintenance mode, meaning: critical bugs will be fixed, and support for new versions of Python will be handled, but new features are not being considered. 2.0.2 (2015-05-27) ------------------ * Issue #20: Fix "paster points --list" on Python 3. * Fix cgi_server on Python 3. * Fix usage of the sort() method on Python 3. * Fix grep on Python 3. 2.0.1 (2015-05-26) ------------------ * Fix --help command on Python 3. Patch written by Atsushi Odagiri (aodag). * Fix "paster create --template=basic_package test" command on Python 3. Patch written by Atsushi Odagiri (aodag). * Fix error when 'paster create --list-template' on Python 3. Patch written by FGtatsuro. * Create universal wheel package. 2.0 (2015-05-26) ---------------- * Experimental Python 3 support. * six module is now required. * Drop support of Python 2.5 and older 1.7.5 ----- * Import CherryPy directly instead of including its server inline in the ``paste.script`` package. You must install CherryPy before using ``egg:PasteScript#cherrypy`` 1.7.4.2 ------- * 1.7.4 had package release problems, was reverted; 1.7.4.1 also had package problems. * Include special ``here`` and ``__file__`` default vars for logging config files, similar to PasteDeploy config loading. * Allow Jython to import various bits from ``paste.script.command`` and ``paste.script.copydir`` without throwing an import error (subprocess module cannot be imported on Jython). This allows PasteScript to work minimally on Jython, although execution will fail for ``command.run_command`` and ``copydir.copydir``. 1.7.3 ----- * CherryPy wsgiserver updated to 3.1.1, fixes a regression in Python 2.5 plus a couple other small fixes. 1.7.2 ----- * Fix a packaging issue that could cause problems when installing PasteScript. 1.7.1 ----- * filemaker.py's FileOp can now handle a tuple as a source_dir argument that should function the same as the _template_dir option for pkg_resources. * CherryPy wsgiserver updated to trunk@2063 for Python 2.6 compatibility. 1.7 --- * _template_dir now takes a tuple argument, which should be the package name, and the relative location to the package of its template directory. pkg_resources will then be used to load make the templates rather than raw file access making it zip-safe. * CherryPy wsgiserver updated to the 3.1.0 release's. * Support Python 2.6. * Added experimental support for a quicker paster serve --reload for Jython. * Non-Python files in ``paste/script/templates/`` causes an error on 2.6; renamed directory to avoid this. 1.6.3 ----- * Fixes for ``paste.script.filemaker`` * A setuptools ``egg_info.writers`` entry point is now provided that's responsible for writing paster_plugins.txt for projects that define a new ``paster_plugins`` setup() keyword. paster_plugins.txt will still be created for new projects that need it and lack a ``paster_plugins`` setup() keyword, but this is deprecated. Projects defining ``paster_plugins`` in setup() should also define a ``setup_requires`` setup() keyword including PasteScript. * An ``egg_plugins`` variable (a list of strings based off the Templates classes' ``egg_plugins`` variables) is now available to paster create templates for the new ``paster_plugins`` setup() keyword. * PasteScript is no longer included in ``egg_plugins``/paster_plugins.txt by default. 1.6.2 ----- * Fix SkipTemplate (could raise ``TypeError: argument 1 must be string or read-only buffer, not None`` before) 1.6.1 (and 1.6.1.1) ------------------- * Fix paster serve under Windows. 1.6 --- * Added commands ``paster request config.ini URL`` and ``paster post config.ini URL < post-body``, that allow you to do artificial requests to applications. * Check the writability of the pid and log files earlier. This caused particular problems if you started it in daemon mode, and the files weren't writable. From Chris Atlee. * Start the monitor (when using ``--monitor``) after daemonizing, so that ``paster serve --monitor --daemon`` works (before it would constantly restart). * In Paste Script templates, you can give ``should_echo=False`` in variable definitions, and if the user is queried for the variable then the input will not be echoed (as for a password). From Dirceu Pereira Tiegs. * Added a method ``paste.script.appinstall.Installer.template_renderer``, which can be used to override template substitution with ``paster make-config``. The function is similar to the same function used with ``paster create`` templates. * Remove ``--daemon`` option from Windows, as it depends on ``os.fork`` * When using ``paster create`` and inserting text with a ``-*-`` marker, multi-line text will no longer be reinserted. * Improved output when skipping templates with ``paster create``. * When starting a server with ``paster serve --daemon`` and the pid file exists and describes a running process, do not start another process. * Added ``umask`` option to ``egg:PasteScript#flup_fcgi_thread/fork``. * Deprecate the flup entry points, as flup now has the necessary entry points in its own package. 1.3.6 ----- * CherryPy wsgiserver updated to the 3.0.2 release's * paster no longer hides ``pkg_resources.DistributionNotFound`` error messages describing the target project's requirements. Aids the somewhat confusing "did you run setup.py develop?" message when it had already been ran, but since then had a requirement added that wasn't installed. * Logging configuration is now read from the config file during ``paster setup-app``. * Custom Formatters and Handlers (Handlers outside of the logging module) are now supported in logging configuration files. 1.3.5 ----- * Initialize logging earlier in the serve command for components that want to utilize it. Patch from Christopher Lenz. * Fixed Python 2.3 incompatibility (no ``string.Template``) in ``paste.script.appinstall``. 1.3.4 ----- * Make sure that when using ``--monitor`` or ``--reload``, if the parent monitoring process dies, also kill the subprocess. * When using ``paster serve --log-file``, append to the log file (was truncating any previous contents). * Read logging information from the server file, using the logging module's `standard configuration format `_ * When adding files don't fail because an svn command fails. Helpful particularly on Windows, when the svn command-line isn't installed (e.g., using TortoiseSVN). 1.3.3 ----- * Fixed problem with ``paster serve`` on Windows. Also on Windows, fixed issue with executables with spaces in their names (this case requires the ``win32all`` module). * You can use ``+dot+`` in your project template filenames, specifically so that you can use leading dots in the filename. Usually leading dots cause the file to be ignored. So if you want to have new projects contain a ``.cvsignore`` file, you can put a ``+dot+cvsignore`` file in your template. * Relatedly, ``+plus+`` has been added so you can include pluses. 1.3.2 ----- * ``paster`` was largely broken under Windows; fixed. 1.3.1 ----- * Fix related to Python 2.5 (when there are errors creating files, you could get infinite recursion under Python 2.5). * Use ``subprocess`` module in ``paster serve`` command. Added ``--monitor`` option which will restart the server if it exits. * The ``exe`` command now does % substitution in keys (e.g., ``pid_file=%(here)s/paste.pid``). * Some import problems with Cheetah should be improved. 1.3 --- * Fixed an exception being raised when shutting down flup servers using sockets. * Fixed the CherryPy 3 WSGI server entry point's handling of SIGHUP and SIGTERM. * The CherryPy wsgiserver is now available at ``paste.script.wsgiserver`` (no longer requiring CherryPy to be installed). * Added entry point for twisted server. * Made ``paste.script.pluginlib:egg_info_dir`` work with packages that put the ``Package.egg-info/`` directory in a subdirectory (typically ``src/``). * Remove Cheetah requirement. Packages using Cheetah templates should require Cheetah themselves. If you are using ``paster make-config`` and you *don't* want to use Cheetah, you must add ``use_cheetah = False`` to your ``Installer`` subclass (it defaults to true for backward compatibility). * Make scripts work when there is no ``setup.py`` (if you aren't making a Python/setuptools package). * When using ``paste.script.copydir.copy_dir`` (as with most ``paster create`` templates), you can raise ``SkipTemplate`` (or call the ``skip_template()`` function) which will cause the template to be skipped. You can use this to conditionally include files. * When using ``paster serve c:/...``, it should no longer confuse ``c:`` with a scheme (such as ``config:`` or ``egg:``). * More careful about catching import errors in ``websetup``, so if you have a bug in your ``app.websetup`` module it won't swallow it. 1.1 --- * Added a ``warn_returncode`` option to ``Command.run_command``, and make ``ensure_file`` use this for its svn command. * If the svn command-line program isn't working for you, commands that use ``ensure_file`` will continue to work but just with a warning. * Removed copyright notice that was accidentally included in new packages created by ``paster create``. * Allow variable assignments at the end of ``paster serve``, like ``paster serve http_port=80``; then you can use ``%(http_port)s`` in your config files (requires up-to-date Paste Deploy). * Allow a ``package_dir`` variable so that you can put your package into subdirectories of the base directory (e.g., ``src/``). * Changes to the ``twisted.web2`` wrapper (from pythy). * Warn when you run ``paster setup-app`` and no modules are listed in ``top_level.txt`` (which can happen with a newly checked out project). 1.0 --- * Added entry point for CherryPy 3's WSGI server. * Fixed ``paster serve`` to hide KeyboardInterrupt (CTRL-C) tracebacks in ``--reload`` mode. * Added ``template_renderer`` argument to ``paste.script.copydir.copydir``. This allows you to use arbitrary template languages, instead of just ``string.Template`` and Cheetah. 0.9.9 ----- * egg:PasteScript#test (the paste.script.testapp) now accepts ``lint`` and ``text`` boolean configuration. ``lint`` will turn on ``paste.lint`` validation. ``text`` will cause it to return a simple text/plain response with the environ, instead of an HTML table. * Improvements all around to ``paster points``, plus documentation for existing entry point groups. 0.9.8 ----- * New projects will now ignore ``Package.egg-info/dependency_links.txt``, just like all the other derivative files in the ``egg-info`` directory * ``paster serve --reload`` was broken on Windows when the Python executable was in a directory with spaces in it. This is probably a bug in the ``subprocess`` module. 0.9.7 ----- * Update to filemaker commands to take optional argument so that when new directories are for a Python package, they will have a __init__.py created as well. 0.9.6 ----- * Do all variable assignment during package creation up-front, before actually creating the files. * Added the ``egg`` template variable: provides projects with a safe egg name as generated by setuptools. This should be used for egg-info directories in templates (e.g. ``+egg+.egg-info`` instead of ``+project+.egg-info``), and anywhere else the egg name is expected, to prevent errors with project names containing hyphens. 0.9 --- * Installer calls ``websetup.setup_app(command, conf, vars)``; ``setup_config()`` will be deprecated in the future * Added copyright information * ``paster serve config.ini#section`` works now * ``paster make-config/setup-app`` will read ``$PASTE_SYSCONFIG`` to find extra ``sysconfig.py`` files. * ``paster create`` will now query interactively for variables if they aren't explicitly provided. 0.5 --- * If the output directory doesn't exist when running ``paster create``, do not default to having interactive (confirmation) on. 0.4.2 ----- * Fixed the Flup FastCGI interface. (There seem to still be problems with forking FastCGI.) * The ``prepare-app`` command has been renamed ``make-config`` * Changed the way ``make-config`` and ``setup-app`` use ``sysconfig`` -- these are now modules that can define various functions * Allow for default config file names * Consider config generation that may produce a directory (this case is now generally workable) * Allow for multiple config files (specifically with --edit) * Give config file generation the variables ``app_install_uuid`` and ``app_install_secret`` that they can use for their config files * Include Ka-Ping Yee's uuid module in ``paste.script.util.uuid`` * ``paster help`` doesn't bail when commands can't be loaded * Be a little safer when ``--edit`` fails and ``--setup`` is provided (don't automatically set up if the edit seems to have failed) 0.4.1 ----- * Two small bugfixes, one related to the ``basic_package`` template (it included a reference to ``finddata``, which it should not have), and a fix to how the ``.egg-info`` directory is determined. 0.4 --- * Added ``points`` command, for entry-point related queries. * paste.deploy config files that start with ``#!/usr/bin/env paster`` can make a script into an executable. * Improvements to ``paster serve`` command: - Handle bad PID files better - Daemonization is more reliable - Allow ``start``, ``stop``, ``restart`` instead of just options * Improvements to ``paster create`` command: - Invoked interactively by default (so that you are warned before overwriting files) * Added new commands: - ``points`` for viewing Egg entry point information - ``prepare-app`` and ``setup-app`` for installing web applications * Fixed bug in how Egg distributions are loaded. 0.3.1 ----- * Fixed small bug with running ``paster serve`` on Windows. (Small to fix, kept script from running on Windows entirely). 0.3 --- Initial release. pastescript-3.7.0/paste/000077500000000000000000000000001474421447600152135ustar00rootroot00000000000000pastescript-3.7.0/paste/__init__.py000066400000000000000000000010501474421447600173200ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php try: import pkg_resources pkg_resources.declare_namespace(__name__) except ImportError: # don't prevent use of paste if pkg_resources isn't installed from pkgutil import extend_path __path__ = extend_path(__path__, __name__) try: import modulefinder except ImportError: pass else: for p in __path__: modulefinder.AddPackagePath(__name__, p) pastescript-3.7.0/paste/script/000077500000000000000000000000001474421447600165175ustar00rootroot00000000000000pastescript-3.7.0/paste/script/__init__.py000066400000000000000000000002531474421447600206300ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php # pastescript-3.7.0/paste/script/appinstall.py000066400000000000000000000603011474421447600212400ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php """ Provides the two commands for preparing an application: ``prepare-app`` and ``setup-app`` """ import types import os import string import uuid from paste.deploy import appconfig from paste.script import copydir from paste.script.command import Command, BadCommand, run as run_command from paste.script.util import secret from paste.util import import_string import paste.script.templates import pkg_resources Cheetah = None class AbstractInstallCommand(Command): default_interactive = 1 default_sysconfigs = [ (False, '/etc/paste/sysconfig.py'), (False, '/usr/local/etc/paste/sysconfig.py'), (True, 'paste.script.default_sysconfig'), ] if os.environ.get('HOME'): default_sysconfigs.insert( 0, (False, os.path.join(os.environ['HOME'], '.paste', 'config', 'sysconfig.py'))) if os.environ.get('PASTE_SYSCONFIG'): default_sysconfigs.insert( 0, (False, os.environ['PASTE_SYSCONFIG'])) def run(self, args): # This is overridden so we can parse sys-config before we pass # it to optparse self.sysconfigs = self.default_sysconfigs new_args = [] while args: if args[0].startswith('--no-default-sysconfig'): self.sysconfigs = [] args.pop(0) continue if args[0].startswith('--sysconfig='): self.sysconfigs.insert( 0, (True, args.pop(0)[len('--sysconfig='):])) continue if args[0] == '--sysconfig': args.pop(0) if not args: raise BadCommand( "You gave --sysconfig as the last argument without " "a value") self.sysconfigs.insert(0, (True, args.pop(0))) continue new_args.append(args.pop(0)) self.load_sysconfigs() return super(AbstractInstallCommand, self).run(new_args) #@classmethod def standard_parser(cls, **kw): parser = super(AbstractInstallCommand, cls).standard_parser(**kw) parser.add_option('--sysconfig', action="append", dest="sysconfigs", help="System configuration file") parser.add_option('--no-default-sysconfig', action='store_true', dest='no_default_sysconfig', help="Don't load the default sysconfig files") parser.add_option( '--easy-install', action='append', dest='easy_install_op', metavar='OP', help='An option to add if invoking easy_install (like --easy-install=exclude-scripts)') parser.add_option( '--no-install', action='store_true', dest='no_install', help="Don't try to install the package (it must already be installed)") parser.add_option( '-f', '--find-links', action='append', dest='easy_install_find_links', metavar='URL', help='Passed through to easy_install') return parser standard_parser = classmethod(standard_parser) ######################################## ## Sysconfig Handling ######################################## def load_sysconfigs(self): configs = self.sysconfigs[:] configs.reverse() self.sysconfig_modules = [] for index, (explicit, name) in enumerate(configs): # @@: At some point I'd like to give the specialized # modules some access to the values in earlier modules, # e.g., to specialize those values or functions. That's # why these modules are loaded backwards. if name.endswith('.py'): if not os.path.exists(name): if explicit: raise BadCommand( "sysconfig file %s does not exist" % name) else: continue globs = {} exec(compile(open(name).read(), name, 'exec'), globs) mod = types.ModuleType('__sysconfig_%i__' % index) for name, value in globs.items(): setattr(mod, name, value) mod.__file__ = name else: try: mod = import_string.simple_import(name) except ImportError: if explicit: raise else: continue mod.paste_command = self self.sysconfig_modules.insert(0, mod) # @@: I'd really prefer to clone the parser here somehow, # not to modify it in place parser = self.parser self.call_sysconfig_functions('add_custom_options', parser) def get_sysconfig_option(self, name, default=None): """ Return the value of the given option in the first sysconfig module in which it is found, or ``default`` (None) if not found in any. """ for mod in self.sysconfig_modules: if hasattr(mod, name): return getattr(mod, name) return default def get_sysconfig_options(self, name): """ Return the option value for the given name in all the sysconfig modules in which is is found (``[]`` if none). """ return [getattr(mod, name) for mod in self.sysconfig_modules if hasattr(mod, name)] def call_sysconfig_function(self, name, *args, **kw): """ Call the specified function in the first sysconfig module it is defined in. ``NameError`` if no function is found. """ val = self.get_sysconfig_option(name) if val is None: raise NameError( "Method %s not found in any sysconfig module" % name) return val(*args, **kw) def call_sysconfig_functions(self, name, *args, **kw): """ Call all the named functions in the sysconfig modules, returning a list of the return values. """ return [method(*args, **kw) for method in self.get_sysconfig_options(name)] def sysconfig_install_vars(self, installer): """ Return the folded results of calling the ``install_variables()`` functions. """ result = {} all_vars = self.call_sysconfig_functions( 'install_variables', installer) all_vars.reverse() for vardict in all_vars: result.update(vardict) return result ######################################## ## Distributions ######################################## def get_distribution(self, req): """ This gets a distribution object, and installs the distribution if required. """ try: dist = pkg_resources.get_distribution(req) if self.verbose: print('Distribution already installed:') print(' ', dist, 'from', dist.location) return dist except pkg_resources.DistributionNotFound: if self.options.no_install: print("Because --no-install was given, we won't try to install the package %s" % req) raise options = ['-v', '-m'] for op in self.options.easy_install_op or []: if not op.startswith('-'): op = '--'+op options.append(op) for op in self.options.easy_install_find_links or []: options.append('--find-links=%s' % op) if self.simulate: raise BadCommand( "Must install %s, but in simulation mode" % req) print("Must install %s" % req) from setuptools.command import easy_install from setuptools import setup setup(script_args=['-q', 'easy_install'] + options + [req]) return pkg_resources.get_distribution(req) def get_installer(self, distro, ep_group, ep_name): if hasattr(distro, 'load_entry_point'): # pkg_resources.EggInfoDistribution installer_class = distro.load_entry_point( 'paste.app_install', ep_name) else: # importlib.metadata.PathDistribution eps = [ep for ep in distro.entry_points if ep.group == 'paste.app_install' and ep.name == ep_name] installer_class = eps[0].load() installer = installer_class( distro, ep_group, ep_name) return installer class MakeConfigCommand(AbstractInstallCommand): default_verbosity = 1 max_args = None min_args = 1 summary = "Install a package and create a fresh config file/directory" usage = "PACKAGE_NAME [CONFIG_FILE] [VAR=VALUE]" description = """\ Note: this is an experimental command, and it will probably change in several ways by the next release. make-config is part of a two-phase installation process (the second phase is setup-app). make-config installs the package (using easy_install) and asks it to create a bare configuration file or directory (possibly filling in defaults from the extra variables you give). """ parser = AbstractInstallCommand.standard_parser( simulate=True, quiet=True, no_interactive=True) parser.add_option('--info', action="store_true", dest="show_info", help="Show information on the package (after installing it), but do not write a config.") parser.add_option('--name', action='store', dest='ep_name', help='The name of the application contained in the distribution (default "main")') parser.add_option('--entry-group', action='store', dest='ep_group', default='paste.app_factory', help='The entry point group to install (i.e., the kind of application; default paste.app_factory') parser.add_option('--edit', action='store_true', dest='edit', help='Edit the configuration file after generating it (using $EDITOR)') parser.add_option('--setup', action='store_true', dest='run_setup', help='Run setup-app immediately after generating (and possibly editing) the configuration file') def command(self): self.requirement = self.args[0] if '#' in self.requirement: if self.options.ep_name is not None: raise BadCommand( "You may not give both --name and a requirement with " "#name") self.requirement, self.options.ep_name = self.requirement.split('#', 1) if not self.options.ep_name: self.options.ep_name = 'main' self.distro = self.get_distribution(self.requirement) self.installer = self.get_installer( self.distro, self.options.ep_group, self.options.ep_name) if self.options.show_info: if len(self.args) > 1: raise BadCommand( "With --info you can only give one argument") return self.show_info() if len(self.args) < 2: # See if sysconfig can give us a default filename options = filter(None, self.call_sysconfig_functions( 'default_config_filename', self.installer)) if not options: raise BadCommand( "You must give a configuration filename") self.config_file = options[0] else: self.config_file = self.args[1] self.check_config_file() self.project_name = self.distro.project_name self.vars = self.sysconfig_install_vars(self.installer) self.vars.update(self.parse_vars(self.args[2:])) self.vars['project_name'] = self.project_name self.vars['requirement'] = self.requirement self.vars['ep_name'] = self.options.ep_name self.vars['ep_group'] = self.options.ep_group self.vars.setdefault('app_name', self.project_name.lower()) self.vars.setdefault('app_instance_uuid', uuid.uuid4()) self.vars.setdefault('app_instance_secret', secret.secret_string()) if self.verbose > 1: print_vars = sorted(self.vars.items()) print('Variables for installation:') for name, value in print_vars: print(' %s: %r' % (name, value)) self.installer.write_config(self, self.config_file, self.vars) edit_success = True if self.options.edit: edit_success = self.run_editor() setup_configs = self.installer.editable_config_files(self.config_file) # @@: We'll just assume the first file in the list is the one # that works with setup-app... setup_config = setup_configs[0] if self.options.run_setup: if not edit_success: print('Config-file editing was not successful.') if self.ask('Run setup-app anyway?', default=False): self.run_setup(setup_config) else: self.run_setup(setup_config) else: filenames = self.installer.editable_config_files(self.config_file) assert not isinstance(filenames, str), ( "editable_config_files returned a string, not a list") if not filenames and filenames is not None: print('No config files need editing') else: print('Now you should edit the config files') if filenames: for fn in filenames: print(' %s' % fn) def show_info(self): text = self.installer.description(None) print(text) def check_config_file(self): if self.installer.expect_config_directory is None: return fn = self.config_file if self.installer.expect_config_directory: if os.path.splitext(fn)[1]: raise BadCommand( "The CONFIG_FILE argument %r looks like a filename, " "and a directory name is expected" % fn) else: if fn.endswith('/') or not os.path.splitext(fn): raise BadCommand( "The CONFIG_FILE argument %r looks like a directory " "name and a filename is expected" % fn) def run_setup(self, filename): run_command(['setup-app', filename]) def run_editor(self): filenames = self.installer.editable_config_files(self.config_file) if filenames is None: print('Warning: the config file is not known (--edit ignored)') return False if not filenames: print('Warning: no config files need editing (--edit ignored)') return True if len(filenames) > 1: print('Warning: there is more than one editable config file (--edit ignored)') return False if not os.environ.get('EDITOR'): print('Error: you must set $EDITOR if using --edit') return False if self.verbose: print('%s %s' % (os.environ['EDITOR'], filenames[0])) retval = os.system('$EDITOR %s' % filenames[0]) if retval: print('Warning: editor %s returned with error code %i' % ( os.environ['EDITOR'], retval)) return False return True class SetupCommand(AbstractInstallCommand): default_verbosity = 1 max_args = 1 min_args = 1 summary = "Setup an application, given a config file" usage = "CONFIG_FILE" description = """\ Note: this is an experimental command, and it will probably change in several ways by the next release. Setup an application according to its configuration file. This is the second part of a two-phase web application installation process (the first phase is prepare-app). The setup process may consist of things like creating directories and setting up databases. """ parser = AbstractInstallCommand.standard_parser( simulate=True, quiet=True, interactive=True) parser.add_option('--name', action='store', dest='section_name', default=None, help='The name of the section to set up (default: app:main)') def command(self): config_spec = self.args[0] section = self.options.section_name if section is None: if '#' in config_spec: config_spec, section = config_spec.split('#', 1) else: section = 'main' if not ':' in section: plain_section = section section = 'app:'+section else: plain_section = section.split(':', 1)[0] if not config_spec.startswith('config:'): config_spec = 'config:' + config_spec if plain_section != 'main': config_spec += '#' + plain_section config_file = config_spec[len('config:'):].split('#', 1)[0] config_file = os.path.join(os.getcwd(), config_file) self.logging_file_config(config_file) conf = appconfig(config_spec, relative_to=os.getcwd()) ep_name = conf.context.entry_point_name ep_group = conf.context.protocol dist = conf.context.distribution if dist is None: raise BadCommand( "The section %r is not the application (probably a filter). You should add #section_name, where section_name is the section that configures your application" % plain_section) installer = self.get_installer(dist, ep_group, ep_name) installer.setup_config( self, config_file, section, self.sysconfig_install_vars(installer)) self.call_sysconfig_functions( 'post_setup_hook', installer, config_file) class Installer(object): """ Abstract base class for installers, and also a generic installer that will run off config files in the .egg-info directory of a distribution. Packages that simply refer to this installer can provide a file ``*.egg-info/paste_deploy_config.ini_tmpl`` that will be interpreted by Cheetah. They can also provide ``websetup`` modules with a ``setup_app(command, conf, vars)`` (or the now-deprecated ``setup_config(command, filename, section, vars)``) function, that will be called. In the future other functions or configuration files may be called. """ # If this is true, then try to detect filename-looking config_file # values, and reject them. Conversely, if false try to detect # directory-looking values and reject them. None means don't # check. expect_config_directory = False # Set this to give a default config filename when none is # specified: default_config_filename = None # Set this to true to use Cheetah to fill your templates, or false # to not do so: use_cheetah = True def __init__(self, dist, ep_group, ep_name): self.dist = dist self.ep_group = ep_group self.ep_name = ep_name def description(self, config): return 'An application' def write_config(self, command, filename, vars): """ Writes the content to the filename (directory or single file). You should use the ``command`` object, which respects things like simulation and interactive. ``vars`` is a dictionary of user-provided variables. """ command.ensure_file(filename, self.config_content(command, vars)) def editable_config_files(self, filename): """ Return a list of filenames; this is primarily used when the filename is treated as a directory and several configuration files are created. The default implementation returns the file itself. Return None if you don't know what files should be edited on installation. """ if not self.expect_config_directory: return [filename] else: return None def config_content(self, command, vars): """ Called by ``self.write_config``, this returns the text content for the config file, given the provided variables. The default implementation reads ``Package.egg-info/paste_deploy_config.ini_tmpl`` and fills it with the variables. """ global Cheetah meta_name = 'paste_deploy_config.ini_tmpl' if not self.dist.has_metadata(meta_name): if command.verbose: print('No %s found' % meta_name) return self.simple_config(vars) return self.template_renderer( self.dist.get_metadata(meta_name), vars, filename=meta_name) def template_renderer(self, content, vars, filename=None): """ Subclasses may override this to provide different template substitution (e.g., use a different template engine). """ if self.use_cheetah: import Cheetah.Template tmpl = Cheetah.Template.Template(content, searchList=[vars]) return copydir.careful_sub( tmpl, vars, filename) else: tmpl = string.Template(content) return tmpl.substitute(vars) def simple_config(self, vars): """ Return a very simple configuration file for this application. """ if self.ep_name != 'main': ep_name = '#'+self.ep_name else: ep_name = '' return ('[app:main]\n' 'use = egg:%s%s\n' % (self.dist.project_name, ep_name)) def setup_config(self, command, filename, section, vars): """ Called to setup an application, given its configuration file/directory. The default implementation calls ``package.websetup.setup_config(command, filename, section, vars)`` or ``package.websetup.setup_app(command, config, vars)`` With ``setup_app`` the ``config`` object is a dictionary with the extra attributes ``global_conf``, ``local_conf`` and ``filename`` """ if hasattr(self.dist, 'get_metadata_lines'): # pkg_resources.EggInfoDistribution lines = self.dist.get_metadata_lines('top_level.txt') else: # importlib.metadata.PathDistribution lines = self.dist.read_text('top_level.txt').splitlines() modules = [ line.strip() for line in lines if line.strip() and not line.strip().startswith('#')] if not modules: print('No modules are listed in top_level.txt') print('Try running python setup.py egg_info to regenerate that file') for mod_name in modules: mod_name = mod_name + '.websetup' mod = import_string.try_import_module(mod_name) if mod is None: continue if hasattr(mod, 'setup_app'): if command.verbose: print('Running setup_app() from %s' % mod_name) self._call_setup_app( mod.setup_app, command, filename, section, vars) elif hasattr(mod, 'setup_config'): if command.verbose: print('Running setup_config() from %s' % mod_name) mod.setup_config(command, filename, section, vars) else: print('No setup_app() or setup_config() function in %s (%s)' % ( mod.__name__, mod.__file__)) def _call_setup_app(self, func, command, filename, section, vars): filename = os.path.abspath(filename) if ':' in section: section = section.split(':', 1)[1] conf = 'config:%s#%s' % (filename, section) conf = appconfig(conf) conf.filename = filename func(command, conf, vars) pastescript-3.7.0/paste/script/bool_optparse.py000066400000000000000000000037011474421447600217420ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php """ A subclass of ``optparse.OptionParser`` that allows boolean long options (like ``--verbose``) to also take arguments (like ``--verbose=true``). Arguments *must* use ``=``. """ import optparse try: _ = optparse._ except AttributeError: from gettext import gettext as _ class BoolOptionParser(optparse.OptionParser): def _process_long_opt(self, rargs, values): arg = rargs.pop(0) # Value explicitly attached to arg? Pretend it's the next # argument. if "=" in arg: (opt, next_arg) = arg.split("=", 1) rargs.insert(0, next_arg) had_explicit_value = True else: opt = arg had_explicit_value = False opt = self._match_long_opt(opt) option = self._long_opt[opt] if option.takes_value(): nargs = option.nargs if len(rargs) < nargs: if nargs == 1: self.error(_("%s option requires an argument") % opt) else: self.error(_("%s option requires %d arguments") % (opt, nargs)) elif nargs == 1: value = rargs.pop(0) else: value = tuple(rargs[0:nargs]) del rargs[0:nargs] elif had_explicit_value: value = rargs[0].lower().strip() del rargs[0:1] if value in ('true', 'yes', 'on', '1', 'y', 't'): value = None elif value in ('false', 'no', 'off', '0', 'n', 'f'): # Don't process return else: self.error(_('%s option takes a boolean value only (true/false)') % opt) else: value = None option.process(opt, value, values, self) pastescript-3.7.0/paste/script/cgi_server.py000066400000000000000000000045241474421447600212260ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php import os import sys ## FIXME: this should be deprecated in favor of wsgiref def paste_run_cgi(wsgi_app, global_conf): run_with_cgi(wsgi_app) stdout = sys.__stdout__ stdout = stdout.buffer # Taken from the WSGI spec: def run_with_cgi(application): environ = dict(os.environ.items()) environ['wsgi.input'] = sys.stdin environ['wsgi.errors'] = sys.stderr environ['wsgi.version'] = (1,0) environ['wsgi.multithread'] = False environ['wsgi.multiprocess'] = True environ['wsgi.run_once'] = True if environ.get('HTTPS','off') in ('on','1'): environ['wsgi.url_scheme'] = 'https' else: environ['wsgi.url_scheme'] = 'http' headers_set = [] headers_sent = [] def write(data): if not headers_set: raise AssertionError("write() before start_response()") elif not headers_sent: # Before the first output, send the stored headers status, response_headers = headers_sent[:] = headers_set line = 'Status: %s\r\n' % status line = line.encode('utf-8') stdout.write(line) for header in response_headers: line = '%s: %s\r\n' % header line = line.encode('utf-8') stdout.write(line) stdout.write(b'\r\n') stdout.write(data) stdout.flush() def start_response(status,response_headers,exc_info=None): if exc_info: try: if headers_sent: # Re-raise original exception if headers sent raise exc_info finally: exc_info = None # avoid dangling circular ref elif headers_set: raise AssertionError("Headers already set!") headers_set[:] = [status,response_headers] return write result = application(environ, start_response) try: for data in result: if data: # don't send headers until body appears write(data) if not headers_sent: write('') # send headers now if body was empty finally: if hasattr(result,'close'): result.close() pastescript-3.7.0/paste/script/checkperms.py000066400000000000000000000320771474421447600212260ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php """ This is a module to check the filesystem for the presence and permissions of certain files. It can also be used to correct the permissions (but not existance) of those files. Currently only supports Posix systems (with Posixy permissions). Permission stuff can probably be stubbed out later. """ import os def read_perm_spec(spec): """ Reads a spec like 'rw-r--r--' into a octal number suitable for chmod. That is characters in groups of three -- first group is user, second for group, third for other (all other people). The characters are r (read), w (write), and x (executable), though the executable can also be s (sticky). Files in sticky directories get the directories permission setting. Examples:: >>> print oct(read_perm_spec('rw-r--r--')) 0o644 >>> print oct(read_perm_spec('rw-rwsr--')) 0o2664 >>> print oct(read_perm_spec('r-xr--r--')) 0o544 >>> print oct(read_perm_spec('r--------')) 0o400 """ total_mask = 0 # suid/sgid modes give this mask in user, group, other mode: set_bits = (0o4000, 0o2000, 0) pieces = (spec[0:3], spec[3:6], spec[6:9]) for i, (mode, set_bit) in enumerate(zip(pieces, set_bits)): mask = 0 read, write, exe = list(mode) if read == 'r': mask = mask | 4 elif read != '-': raise ValueError( "Character %r unexpected (should be '-' or 'r')" % read) if write == 'w': mask = mask | 2 elif write != '-': raise ValueError( "Character %r unexpected (should be '-' or 'w')" % write) if exe == 'x': mask = mask | 1 elif exe not in ('s', '-'): raise ValueError( "Character %r unexpected (should be '-', 'x', or 's')" % exe) if exe == 's' and i == 2: raise ValueError(( "The 'other' executable setting cannot be suid/sgid ('s')")) mask = mask << ((2-i)*3) if exe == 's': mask = mask | set_bit total_mask = total_mask | mask return total_mask modes = [ (0o4000, 'setuid bit', 'setuid bit: make contents owned by directory owner'), (0o2000, 'setgid bit', 'setgid bit: make contents inherit permissions from directory'), (0o1000, 'sticky bit', 'sticky bit: append-only directory'), (0o0400, 'read by owner', 'read by owner'), (0o0200, 'write by owner', 'write by owner'), (0o0100, 'execute by owner', 'owner can search directory'), (0o0040, 'allow read by group members', 'allow read by group members',), (0o0020, 'allow write by group members', 'allow write by group members'), (0o0010, 'execute by group members', 'group members can search directory'), (0o0004, 'read by others', 'read by others'), (0o0002, 'write by others', 'write by others'), (0o0001, 'execution by others', 'others can search directory'), ] exe_bits = [0o100, 0o010, 0o001] exe_mask = 0o111 full_mask = 0o7777 def mode_diff(filename, mode, **kw): """ Returns the differences calculated using ``calc_mode_diff`` """ cur_mode = os.stat(filename).st_mode return calc_mode_diff(cur_mode, mode, **kw) def calc_mode_diff(cur_mode, mode, keep_exe=True, not_set='not set: ', set='set: '): """ Gives the difference between the actual mode of the file and the given mode. If ``keep_exe`` is true, then if the mode doesn't include any executable information the executable information will simply be ignored. High bits are also always ignored (except suid/sgid and sticky bit). Returns a list of differences (empty list if no differences) """ for exe_bit in exe_bits: if mode & exe_bit: keep_exe = False diffs = [] isdir = os.path.isdir(filename) for bit, file_desc, dir_desc in modes: if keep_exe and bit in exe_bits: continue if isdir: desc = dir_desc else: desc = file_desc if (mode & bit) and not (cur_mode & bit): diffs.append(not_set + desc) if not (mode & bit) and (cur_mode & bit): diffs.append(set + desc) return diffs def calc_set_mode(cur_mode, mode, keep_exe=True): """ Calculates the new mode given the current node ``cur_mode`` and the mode spec ``mode`` and if ``keep_exe`` is true then also keep the executable bits in ``cur_mode`` if ``mode`` has no executable bits in it. Return the new mode. Examples:: >>> print oct(calc_set_mode(0o775, 0o644)) 0o755 >>> print oct(calc_set_mode(0o775, 0o744)) 0o744 >>> print oct(calc_set_mode(0o10600, 0o644)) 0o10644 >>> print oct(calc_set_mode(0o775, 0o644, False)) 0o644 """ for exe_bit in exe_bits: if mode & exe_bit: keep_exe = False # This zeros-out full_mask parts of the current mode: keep_parts = (cur_mode | full_mask) ^ full_mask if keep_exe: keep_parts = keep_parts | (cur_mode & exe_mask) new_mode = keep_parts | mode return new_mode def set_mode(filename, mode, **kw): """ Sets the mode on ``filename`` using ``calc_set_mode`` """ cur_mode = os.stat(filename).st_mode new_mode = calc_set_mode(cur_mode, mode, **kw) os.chmod(filename, new_mode) def calc_ownership_spec(spec): """ Calculates what a string spec means, returning (uid, username, gid, groupname), where there can be None values meaning no preference. The spec is a string like ``owner:group``. It may use numbers instead of user/group names. It may leave out ``:group``. It may use '-' to mean any-user/any-group. """ import grp import pwd user = group = None uid = gid = None if ':' in spec: user_spec, group_spec = spec.split(':', 1) else: user_spec, group_spec = spec, '-' if user_spec == '-': user_spec = '0' if group_spec == '-': group_spec = '0' try: uid = int(user_spec) except ValueError: uid = pwd.getpwnam(user_spec) user = user_spec else: if not uid: uid = user = None else: user = pwd.getpwuid(uid).pw_name try: gid = int(group_spec) except ValueError: gid = grp.getgrnam(group_spec) group = group_spec else: if not gid: gid = group = None else: group = grp.getgrgid(gid).gr_name return (uid, user, gid, group) def ownership_diff(filename, spec): """ Return a list of differences between the ownership of ``filename`` and the spec given. """ import grp import pwd diffs = [] uid, user, gid, group = calc_ownership_spec(spec) st = os.stat(filename) if uid and uid != st.st_uid: diffs.append('owned by %s (should be %s)' % (pwd.getpwuid(st.st_uid).pw_name, user)) if gid and gid != st.st_gid: diffs.append('group %s (should be %s)' % (grp.getgrgid(st.st_gid).gr_name, group)) return diffs def set_ownership(filename, spec): """ Set the ownership of ``filename`` given the spec. """ uid, user, gid, group = calc_ownership_spec(spec) st = os.stat(filename) if not uid: uid = st.st_uid if not gid: gid = st.st_gid os.chmod(filename, uid, gid) class PermissionSpec(object): """ Represents a set of specifications for permissions. Typically reads from a file that looks like this:: rwxrwxrwx user:group filename If the filename ends in /, then it expected to be a directory, and the directory is made executable automatically, and the contents of the directory are given the same permission (recursively). By default the executable bit on files is left as-is, unless the permissions specifically say it should be on in some way. You can use 'nomodify filename' for permissions to say that any permission is okay, and permissions should not be changed. Use 'noexist filename' to say that a specific file should not exist. Use 'symlink filename symlinked_to' to assert a symlink destination The entire file is read, and most specific rules are used for each file (i.e., a rule for a subdirectory overrides the rule for a superdirectory). Order does not matter. """ def __init__(self): self.paths = {} def parsefile(self, filename): f = open(filename) lines = f.readlines() f.close() self.parselines(lines, filename=filename) commands = {} def parselines(self, lines, filename=None): for lineindex, line in enumerate(lines): line = line.strip() if not line or line.startswith('#'): continue parts = line.split() command = parts[0] if command in self.commands: cmd = self.commands[command](*parts[1:]) else: cmd = self.commands['*'](*parts) self.paths[cmd.path] = cmd def check(self): action = _Check(self) self.traverse(action) def fix(self): action = _Fixer(self) self.traverse(action) def traverse(self, action): paths = self.paths_sorted() checked = {} for path, checker in list(paths)[::-1]: self.check_tree(action, path, paths, checked) for path, checker in paths: if path not in checked: action.noexists(path, checker) def traverse_tree(self, action, path, paths, checked): if path in checked: return self.traverse_path(action, path, paths, checked) if os.path.isdir(path): for fn in os.listdir(path): fn = os.path.join(path, fn) self.traverse_tree(action, fn, paths, checked) def traverse_path(self, action, path, paths, checked): checked[path] = None for check_path, checker in paths: if path.startswith(check_path): action.check(check_path, checker) if not checker.inherit: break def paths_sorted(self): paths = sorted(self.paths.items(), key=lambda key_value: len(key_value[0]), reversed=True) class _Rule(object): class __metaclass__(type): def __new__(meta, class_name, bases, d): cls = type.__new__(meta, class_name, bases, d) PermissionSpec.commands[cls.__name__] = cls return cls inherit = False def noexists(self): return ['Path %s does not exist' % path] class _NoModify(_Rule): name = 'nomodify' def __init__(self, path): self.path = path def fix(self, path): pass class _NoExist(_Rule): name = 'noexist' def __init__(self, path): self.path = path def check(self, path): return ['Path %s should not exist' % path] def noexists(self, path): return [] def fix(self, path): # @@: Should delete? pass class _SymLink(_Rule): name = 'symlink' inherit = True def __init__(self, path, dest): self.path = path self.dest = dest def check(self, path): assert path == self.path, ( "_Symlink should only be passed specific path %s (not %s)" % (self.path, path)) try: link = os.path.readlink(path) except OSError as e: if e.errno != 22: raise return ['Path %s is not a symlink (should point to %s)' % (path, self.dest)] if link != self.dest: return ['Path %s should symlink to %s, not %s' % (path, self.dest, link)] return [] def fix(self, path): assert path == self.path, ( "_Symlink should only be passed specific path %s (not %s)" % (self.path, path)) if not os.path.exists(path): os.symlink(path, self.dest) else: # @@: This should correct the symlink or something: print('Not symlinking %s' % path) class _Permission(_Rule): name = '*' def __init__(self, perm, owner, dir): self.perm_spec = read_perm_spec(perm) self.owner = owner self.dir = dir def check(self, path): return mode_diff(path, self.perm_spec) def fix(self, path): set_mode(path, self.perm_spec) class _Strategy(object): def __init__(self, spec): self.spec = spec class _Check(_Strategy): def noexists(self, path, checker): checker.noexists(path) def check(self, path, checker): checker.check(path) class _Fixer(_Strategy): def noexists(self, path, checker): pass def check(self, path, checker): checker.fix(path) if __name__ == '__main__': import doctest doctest.testmod() pastescript-3.7.0/paste/script/cherrypy_server.py000066400000000000000000000066101474421447600223270ustar00rootroot00000000000000""" Entry point for CherryPy's WSGI server """ try: from cherrypy import wsgiserver except ImportError: print('=' * 60) print('== You must install CherryPy (pip install cherrypy) to use the egg:PasteScript#cherrypy server') print('=' * 60) raise try: import ssl from cherrypy.wsgiserver.ssl_builtin import BuiltinSSLAdapter except ImportError: builtin = False else: builtin = True def cpwsgi_server(app, global_conf=None, host='127.0.0.1', port=None, ssl_pem=None, protocol_version=None, numthreads=None, server_name=None, max=None, request_queue_size=None, timeout=None): """ Serves the specified WSGI app via CherryPyWSGIServer. ``app`` The WSGI 'application callable'; multiple WSGI applications may be passed as (script_name, callable) pairs. ``host`` This is the ipaddress to bind to (or a hostname if your nameserver is properly configured). This defaults to 127.0.0.1, which is not a public interface. ``port`` The port to run on, defaults to 8080 for HTTP, or 4443 for HTTPS. This can be a string or an integer value. ``ssl_pem`` This an optional SSL certificate file (via OpenSSL) You can generate a self-signed test PEM certificate file as follows: $ openssl genrsa 1024 > host.key $ chmod 400 host.key $ openssl req -new -x509 -nodes -sha1 -days 365 \\ -key host.key > host.cert $ cat host.cert host.key > host.pem $ chmod 400 host.pem ``protocol_version`` The protocol used by the server, by default ``HTTP/1.1``. ``numthreads`` The number of worker threads to create. ``server_name`` The string to set for WSGI's SERVER_NAME environ entry. ``max`` The maximum number of queued requests. (defaults to -1 = no limit). ``request_queue_size`` The 'backlog' argument to socket.listen(); specifies the maximum number of queued connections. ``timeout`` The timeout in seconds for accepted connections. """ is_ssl = False if ssl_pem: port = port or 4443 is_ssl = True if not port: if ':' in host: host, port = host.split(':', 1) else: port = 8080 bind_addr = (host, int(port)) kwargs = {} for var_name in ('numthreads', 'max', 'request_queue_size', 'timeout'): var = locals()[var_name] if var is not None: kwargs[var_name] = int(var) server = wsgiserver.CherryPyWSGIServer(bind_addr, app, server_name=server_name, **kwargs) if is_ssl: if builtin: server.ssl_module = 'builtin' server.ssl_adapter = BuiltinSSLAdapter(ssl_pem, ssl_pem) else: server.ssl_certificate = server.ssl_private_key = ssl_pem if protocol_version: server.protocol = protocol_version try: protocol = is_ssl and 'https' or 'http' if host == '0.0.0.0': print('serving on 0.0.0.0:%s view at %s://127.0.0.1:%s' % \ (port, protocol, port)) else: print("serving on %s://%s:%s" % (protocol, host, port)) server.start() except (KeyboardInterrupt, SystemExit): server.stop() return server pastescript-3.7.0/paste/script/command.py000066400000000000000000000706151474421447600205200ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php import pkg_resources import sys import optparse from . import bool_optparse import os import re import textwrap from . import pluginlib import configparser import getpass try: import subprocess except ImportError: subprocess = None # jython difflib = None from logging.config import fileConfig class BadCommand(Exception): def __init__(self, message, exit_code=2): self.message = message self.exit_code = exit_code Exception.__init__(self, message) def _get_message(self): """Getter for 'message'; needed only to override deprecation in BaseException.""" return self.__message def _set_message(self, value): """Setter for 'message'; needed only to override deprecation in BaseException.""" self.__message = value # BaseException.message has been deprecated since Python 2.6. # To prevent DeprecationWarning from popping up over this # pre-existing attribute, use a new property that takes lookup # precedence. message = property(_get_message, _set_message) class NoDefault(object): pass dist = pkg_resources.get_distribution('PasteScript') python_version = sys.version.splitlines()[0].strip() parser = optparse.OptionParser(add_help_option=False, version='%s from %s (python %s)' % (dist, dist.location, python_version), usage='%prog [paster_options] COMMAND [command_options]') parser.add_option( '--plugin', action='append', dest='plugins', help="Add a plugin to the list of commands (plugins are Egg specs; will also require() the Egg)") parser.add_option( '-h', '--help', action='store_true', dest='do_help', help="Show this help message") parser.disable_interspersed_args() # @@: Add an option to run this in another Python interpreter system_plugins = [] def run(args=None): if (not args and len(sys.argv) >= 2 and os.environ.get('_') and sys.argv[0] != os.environ['_'] and os.environ['_'] == sys.argv[1]): # probably it's an exe execution args = ['exe', os.environ['_']] + sys.argv[2:] if args is None: args = sys.argv[1:] options, args = parser.parse_args(args) options.base_parser = parser system_plugins.extend(options.plugins or []) commands = get_commands() if options.do_help: args = ['help'] + args if not args: print('Usage: %s COMMAND' % sys.argv[0]) args = ['help'] command_name = args[0] if command_name not in commands: command = NotFoundCommand else: command = commands[command_name].load() invoke(command, command_name, options, args[1:]) def parse_exe_file(config): import shlex p = configparser.RawConfigParser() p.read([config]) command_name = 'exe' options = [] if p.has_option('exe', 'command'): command_name = p.get('exe', 'command') if p.has_option('exe', 'options'): options = shlex.split(p.get('exe', 'options')) if p.has_option('exe', 'sys.path'): paths = shlex.split(p.get('exe', 'sys.path')) paths = [os.path.abspath(os.path.join(os.path.dirname(config), p)) for p in paths] for path in paths: pkg_resources.working_set.add_entry(path) sys.path.insert(0, path) args = [command_name, config] + options return args def get_commands(): plugins = system_plugins[:] egg_info_dir = pluginlib.find_egg_info_dir(os.getcwd()) if egg_info_dir: plugins.append(os.path.splitext(os.path.basename(egg_info_dir))[0]) base_dir = os.path.dirname(egg_info_dir) if base_dir not in sys.path: sys.path.insert(0, base_dir) pkg_resources.working_set.add_entry(base_dir) plugins = pluginlib.resolve_plugins(plugins) commands = pluginlib.load_commands_from_plugins(plugins) commands.update(pluginlib.load_global_commands()) return commands def invoke(command, command_name, options, args): try: runner = command(command_name) exit_code = runner.run(args) except BadCommand as e: print(e.message) exit_code = e.exit_code sys.exit(exit_code) class Command(object): def __init__(self, name): self.command_name = name max_args = None max_args_error = 'You must provide no more than %(max_args)s arguments' min_args = None min_args_error = 'You must provide at least %(min_args)s arguments' required_args = None # If this command takes a configuration file, set this to 1 or -1 # Then if invoked through #! the config file will be put into the positional # arguments -- at the beginning with 1, at the end with -1 takes_config_file = None # Grouped in help messages by this: group_name = '' required_args = () description = None usage = '' hidden = False # This is the default verbosity level; --quiet subtracts, # --verbose adds: default_verbosity = 0 # This is the default interactive state: default_interactive = 0 return_code = 0 BadCommand = BadCommand # Must define: # parser # summary # command() def run(self, args): self.parse_args(args) # Setup defaults: for name, default in [('verbose', 0), ('quiet', 0), ('interactive', False), ('overwrite', False)]: if not hasattr(self.options, name): setattr(self.options, name, default) if getattr(self.options, 'simulate', False): self.options.verbose = max(self.options.verbose, 1) self.interactive = self.default_interactive if getattr(self.options, 'interactive', False): self.interactive += self.options.interactive if getattr(self.options, 'no_interactive', False): self.interactive = False self.verbose = self.default_verbosity self.verbose += self.options.verbose self.verbose -= self.options.quiet self.simulate = getattr(self.options, 'simulate', False) # For #! situations: if (os.environ.get('PASTE_CONFIG_FILE') and self.takes_config_file is not None): take = self.takes_config_file filename = os.environ.get('PASTE_CONFIG_FILE') if take == 1: self.args.insert(0, filename) elif take == -1: self.args.append(filename) else: assert 0, ( "Value takes_config_file must be None, 1, or -1 (not %r)" % take) if (os.environ.get('PASTE_DEFAULT_QUIET')): self.verbose = 0 # Validate: if self.min_args is not None and len(self.args) < self.min_args: raise BadCommand( self.min_args_error % {'min_args': self.min_args, 'actual_args': len(self.args)}) if self.max_args is not None and len(self.args) > self.max_args: raise BadCommand( self.max_args_error % {'max_args': self.max_args, 'actual_args': len(self.args)}) for var_name, option_name in self.required_args: if not getattr(self.options, var_name, None): raise BadCommand( 'You must provide the option %s' % option_name) result = self.command() if result is None: return self.return_code else: return result def parse_args(self, args): if self.usage: usage = ' '+self.usage else: usage = '' self.parser.usage = "%%prog [options]%s\n%s" % ( usage, self.summary) self.parser.prog = self._prog_name() if self.description: desc = self.description desc = textwrap.dedent(desc) self.parser.description = desc self.options, self.args = self.parser.parse_args(args) def _prog_name(self): return '%s %s' % (os.path.basename(sys.argv[0]), self.command_name) ######################################## ## Utility methods ######################################## def here(cls): mod = sys.modules[cls.__module__] return os.path.dirname(mod.__file__) here = classmethod(here) def ask(self, prompt, safe=False, default=True): """ Prompt the user. Default can be true, false, ``'careful'`` or ``'none'``. If ``'none'`` then the user must enter y/n. If ``'careful'`` then the user must enter yes/no (long form). If the interactive option is over two (``-ii``) then ``safe`` will be used as a default. This option should be the do-nothing option. """ # @@: Should careful be a separate argument? if self.options.interactive >= 2: default = safe if default == 'careful': prompt += ' [yes/no]?' elif default == 'none': prompt += ' [y/n]?' elif default: prompt += ' [Y/n]? ' else: prompt += ' [y/N]? ' while 1: response = input(prompt).strip().lower() if not response: if default in ('careful', 'none'): print('Please enter yes or no') continue return default if default == 'careful': if response in ('yes', 'no'): return response == 'yes' print('Please enter "yes" or "no"') continue if response[0].lower() in ('y', 'n'): return response[0].lower() == 'y' print('Y or N please') def challenge(self, prompt, default=NoDefault, should_echo=True): """ Prompt the user for a variable. """ if default is not NoDefault: prompt += ' [%r]' % default prompt += ': ' while 1: if should_echo: prompt_method = input else: prompt_method = getpass.getpass response = prompt_method(prompt).strip() if not response: if default is not NoDefault: return default else: continue else: return response def pad(self, s, length, dir='left'): if len(s) >= length: return s if dir == 'left': return s + ' '*(length-len(s)) else: return ' '*(length-len(s)) + s def standard_parser(cls, verbose=True, interactive=False, no_interactive=False, simulate=False, quiet=False, overwrite=False): """ Create a standard ``OptionParser`` instance. Typically used like:: class MyCommand(Command): parser = Command.standard_parser() Subclasses may redefine ``standard_parser``, so use the nearest superclass's class method. """ parser = bool_optparse.BoolOptionParser() if verbose: parser.add_option('-v', '--verbose', action='count', dest='verbose', default=0) if quiet: parser.add_option('-q', '--quiet', action='count', dest='quiet', default=0) if no_interactive: parser.add_option('--no-interactive', action="count", dest="no_interactive", default=0) if interactive: parser.add_option('-i', '--interactive', action='count', dest='interactive', default=0) if simulate: parser.add_option('-n', '--simulate', action='store_true', dest='simulate', default=False) if overwrite: parser.add_option('-f', '--overwrite', dest="overwrite", action="store_true", help="Overwrite files (warnings will be emitted for non-matching files otherwise)") return parser standard_parser = classmethod(standard_parser) def shorten(self, fn, *paths): """ Return a shorted form of the filename (relative to the current directory), typically for displaying in messages. If ``*paths`` are present, then use os.path.join to create the full filename before shortening. """ if paths: fn = os.path.join(fn, *paths) if fn.startswith(os.getcwd()): return fn[len(os.getcwd()):].lstrip(os.path.sep) else: return fn def ensure_dir(self, dir, svn_add=True): """ Ensure that the directory exists, creating it if necessary. Respects verbosity and simulation. Adds directory to subversion if ``.svn/`` directory exists in parent, and directory was created. """ dir = dir.rstrip(os.sep) if not dir: # we either reached the parent-most directory, or we got # a relative directory # @@: Should we make sure we resolve relative directories # first? Though presumably the current directory always # exists. return if not os.path.exists(dir): self.ensure_dir(os.path.dirname(dir)) if self.verbose: print('Creating %s' % self.shorten(dir)) if not self.simulate: os.mkdir(dir) if (svn_add and os.path.exists(os.path.join(os.path.dirname(dir), '.svn'))): self.svn_command('add', dir) else: if self.verbose > 1: print("Directory already exists: %s" % self.shorten(dir)) def ensure_file(self, filename, content, svn_add=True): """ Ensure a file named ``filename`` exists with the given content. If ``--interactive`` has been enabled, this will ask the user what to do if a file exists with different content. """ global difflib assert content is not None, ( "You cannot pass a content of None") self.ensure_dir(os.path.dirname(filename), svn_add=svn_add) if not os.path.exists(filename): if self.verbose: print('Creating %s' % filename) if not self.simulate: f = open(filename, 'wb') f.write(content) f.close() if svn_add and os.path.exists(os.path.join(os.path.dirname(filename), '.svn')): self.svn_command('add', filename, warn_returncode=True) return f = open(filename, 'rb') old_content = f.read() f.close() if content == old_content: if self.verbose > 1: print('File %s matches expected content' % filename) return if not self.options.overwrite: print('Warning: file %s does not match expected content' % filename) if difflib is None: import difflib diff = difflib.context_diff( content.splitlines(), old_content.splitlines(), 'expected ' + filename, filename) print('\n'.join(diff)) if self.interactive: while 1: s = input( 'Overwrite file with new content? [y/N] ').strip().lower() if not s: s = 'n' if s.startswith('y'): break if s.startswith('n'): return print('Unknown response; Y or N please') else: return if self.verbose: print('Overwriting %s with new content' % filename) if not self.simulate: f = open(filename, 'wb') f.write(content) f.close() def insert_into_file(self, filename, marker_name, text, indent=False): """ Inserts ``text`` into the file, right after the given marker. Markers look like: ``-*- [:]? -*-``, and the text will go on the immediately following line. Raises ``ValueError`` if the marker is not found. If ``indent`` is true, then the text will be indented at the same level as the marker. """ if not text.endswith('\n'): raise ValueError( "The text must end with a newline: %r" % text) if not os.path.exists(filename) and self.simulate: # If we are doing a simulation, it's expected that some # files won't exist... if self.verbose: print('Would (if not simulating) insert text into %s' % ( self.shorten(filename))) return f = open(filename) lines = f.readlines() f.close() regex = re.compile(r'-\*-\s+%s:?\s+-\*-' % re.escape(marker_name), re.I) for i in range(len(lines)): if regex.search(lines[i]): # Found it! if (lines[i:] and len(lines[i:]) > 1 and ''.join(lines[i+1:]).strip().startswith(text.strip())): # Already have it! print('Warning: line already found in %s (not inserting' % filename) print(' %s' % lines[i]) return if indent: text = text.lstrip() match = re.search(r'^[ \t]*', lines[i]) text = match.group(0) + text lines[i+1:i+1] = [text] break else: errstr = ( "Marker '-*- %s -*-' not found in %s" % (marker_name, filename)) if 1 or self.simulate: # @@: being permissive right now print('Warning: %s' % errstr) else: raise ValueError(errstr) if self.verbose: print('Updating %s' % self.shorten(filename)) if not self.simulate: f = open(filename, 'w') f.write(''.join(lines)) f.close() def run_command(self, cmd, *args, **kw): """ Runs the command, respecting verbosity and simulation. Returns stdout, or None if simulating. Keyword arguments: cwd: the current working directory to run the command in capture_stderr: if true, then both stdout and stderr will be returned expect_returncode: if true, then don't fail if the return code is not 0 force_no_simulate: if true, run the command even if --simulate """ if subprocess is None: raise RuntimeError('Environment does not support subprocess ' 'module, cannot run command.') cmd = self.quote_first_command_arg(cmd) cwd = popdefault(kw, 'cwd', os.getcwd()) capture_stderr = popdefault(kw, 'capture_stderr', False) expect_returncode = popdefault(kw, 'expect_returncode', False) force = popdefault(kw, 'force_no_simulate', False) warn_returncode = popdefault(kw, 'warn_returncode', False) if warn_returncode: expect_returncode = True simulate = self.simulate if force: simulate = False assert not kw, ("Arguments not expected: %s" % kw) if capture_stderr: stderr_pipe = subprocess.STDOUT else: stderr_pipe = subprocess.PIPE try: proc = subprocess.Popen([cmd] + list(args), cwd=cwd, stderr=stderr_pipe, stdout=subprocess.PIPE) except OSError as e: if e.errno != 2: # File not found raise raise OSError( "The expected executable %s was not found (%s)" % (cmd, e)) if self.verbose: print('Running %s %s' % (cmd, ' '.join(args))) if simulate: return None stdout, stderr = proc.communicate() if proc.returncode and not expect_returncode: if not self.verbose: print('Running %s %s' % (cmd, ' '.join(args))) print('Error (exit code: %s)' % proc.returncode) if stderr: print(stderr) raise OSError("Error executing command %s" % cmd) if self.verbose > 2: if stderr: print('Command error output:') print(stderr) if stdout: print('Command output:') print(stdout) elif proc.returncode and warn_returncode: print('Warning: command failed (%s %s)' % (cmd, ' '.join(args))) print('Exited with code %s' % proc.returncode) return stdout def quote_first_command_arg(self, arg): """ There's a bug in Windows when running an executable that's located inside a path with a space in it. This method handles that case, or on non-Windows systems or an executable with no spaces, it just leaves well enough alone. """ if (sys.platform != 'win32' or ' ' not in arg): # Problem does not apply: return arg try: import win32api except ImportError: raise ValueError( "The executable %r contains a space, and in order to " "handle this issue you must have the win32api module " "installed" % arg) arg = win32api.GetShortPathName(arg) return arg _svn_failed = False def svn_command(self, *args, **kw): """ Run an svn command, but don't raise an exception if it fails. """ try: return self.run_command('svn', *args, **kw) except OSError as e: if not self._svn_failed: print('Unable to run svn command (%s); proceeding anyway' % e) self._svn_failed = True def write_file(self, filename, content, source=None, binary=True, svn_add=True): """ Like ``ensure_file``, but without the interactivity. Mostly deprecated. (I think I forgot it existed) """ import warnings warnings.warn( "command.write_file has been replaced with " "command.ensure_file", DeprecationWarning, 2) if os.path.exists(filename): if binary: f = open(filename, 'rb') else: f = open(filename, 'r') old_content = f.read() f.close() if content == old_content: if self.verbose: print('File %s exists with same content' % ( self.shorten(filename))) return if (not self.simulate and self.options.interactive): if not self.ask('Overwrite file %s?' % filename): return if self.verbose > 1 and source: print('Writing %s from %s' % (self.shorten(filename), self.shorten(source))) elif self.verbose: print('Writing %s' % self.shorten(filename)) if not self.simulate: already_existed = os.path.exists(filename) if binary: f = open(filename, 'wb') else: f = open(filename, 'w') f.write(content) f.close() if (not already_existed and svn_add and os.path.exists(os.path.join(os.path.dirname(filename), '.svn'))): self.svn_command('add', filename) def parse_vars(self, args): """ Given variables like ``['a=b', 'c=d']`` turns it into ``{'a': 'b', 'c': 'd'}`` """ result = {} for arg in args: if '=' not in arg: raise BadCommand( 'Variable assignment %r invalid (no "=")' % arg) name, value = arg.split('=', 1) result[name] = value return result def read_vars(self, config, section='pastescript'): """ Given a configuration filename, this will return a map of values. """ result = {} p = configparser.RawConfigParser() p.read([config]) if p.has_section(section): for key, value in p.items(section): if key.endswith('__eval__'): result[key[:-len('__eval__')]] = eval(value) else: result[key] = value return result def write_vars(self, config, vars, section='pastescript'): """ Given a configuration filename, this will add items in the vars mapping to the configuration file. Will create the configuration file if it doesn't exist. """ modified = False p = configparser.RawConfigParser() if not os.path.exists(config): f = open(config, 'w') f.write('') f.close() modified = True p.read([config]) if not p.has_section(section): p.add_section(section) modified = True existing_options = p.options(section) for key, value in vars.items(): if (key not in existing_options and '%s__eval__' % key not in existing_options): if not isinstance(value, str): p.set(section, '%s__eval__' % key, repr(value)) else: p.set(section, key, value) modified = True if modified: p.write(open(config, 'w')) def indent_block(self, text, indent=2, initial=None): """ Indent the block of text (each line is indented). If you give ``initial``, then that is used in lieue of ``indent`` for the first line. """ if initial is None: initial = indent lines = text.splitlines() first = (' '*initial) + lines[0] rest = [(' '*indent)+l for l in lines[1:]] return '\n'.join([first]+rest) def logging_file_config(self, config_file): """ Setup logging via the logging module's fileConfig function with the specified ``config_file``, if applicable. ConfigParser defaults are specified for the special ``__file__`` and ``here`` variables, similar to PasteDeploy config loading. """ parser = configparser.ConfigParser() parser.read([config_file]) if parser.has_section('loggers'): config_file = os.path.abspath(config_file) fileConfig(config_file, dict(__file__=config_file, here=os.path.dirname(config_file))) class NotFoundCommand(Command): def run(self, args): #for name, value in os.environ.items(): # print '%s: %s' % (name, value) #print sys.argv print(('Command %r not known (you may need to run setup.py egg_info)' % self.command_name)) commands = sorted(get_commands().items()) if not commands: print('No commands registered.') print('Have you installed Paste Script?') print('(try running python setup.py develop)') return 2 print('Known commands:') longest = max([len(n) for n, c in commands]) for name, command in commands: print(' %s %s' % (self.pad(name, length=longest), command.load().summary)) return 2 def popdefault(dict, name, default=None): if name not in dict: return default else: v = dict[name] del dict[name] return v pastescript-3.7.0/paste/script/copydir.py000066400000000000000000000362761474421447600205600ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php import os import pkg_resources from urllib.parse import quote import string try: import html except ImportError: import cgi as html Cheetah = None try: import subprocess except ImportError: subprocess = None # jython import inspect class SkipTemplate(Exception): """ Raised to indicate that the template should not be copied over. Raise this exception during the substitution of your template """ def copy_dir(source, dest, vars, verbosity, simulate, indent=0, use_cheetah=False, sub_vars=True, interactive=False, svn_add=True, overwrite=True, template_renderer=None): """ Copies the ``source`` directory to the ``dest`` directory. ``vars``: A dictionary of variables to use in any substitutions. ``verbosity``: Higher numbers will show more about what is happening. ``simulate``: If true, then don't actually *do* anything. ``indent``: Indent any messages by this amount. ``sub_vars``: If true, variables in ``_tmpl`` files and ``+var+`` in filenames will be substituted. ``use_cheetah``: If true, then any templates encountered will be substituted with Cheetah. Otherwise ``template_renderer`` or ``string.Template`` will be used for templates. ``svn_add``: If true, any files written out in directories that are part of a svn working copy will be added (via ``svn add``). ``overwrite``: If false, then don't every overwrite anything. ``interactive``: If you are overwriting a file and interactive is true, then ask before overwriting. ``template_renderer``: This is a function for rendering templates (if you don't want to use Cheetah or string.Template). It should have the signature ``template_renderer(content_as_string, vars_as_dict, filename=filename)``. """ # This allows you to use a leading +dot+ in filenames which would # otherwise be skipped because leading dots make the file hidden: vars.setdefault('dot', '.') vars.setdefault('plus', '+') use_pkg_resources = isinstance(source, tuple) if use_pkg_resources: names = pkg_resources.resource_listdir(source[0], source[1]) else: names = os.listdir(source) names.sort() pad = ' '*(indent*2) if not os.path.exists(dest): if verbosity >= 1: print('%sCreating %s/' % (pad, dest)) if not simulate: svn_makedirs(dest, svn_add=svn_add, verbosity=verbosity, pad=pad) elif verbosity >= 2: print('%sDirectory %s exists' % (pad, dest)) for name in names: if use_pkg_resources: full = '/'.join([source[1], name]) else: full = os.path.join(source, name) reason = should_skip_file(name) if reason: if verbosity >= 2: reason = pad + reason % {'filename': full} print(reason) continue if sub_vars: dest_full = os.path.join(dest, substitute_filename(name, vars)) sub_file = False if dest_full.endswith('_tmpl'): dest_full = dest_full[:-5] sub_file = sub_vars if use_pkg_resources and pkg_resources.resource_isdir(source[0], full): if verbosity: print('%sRecursing into %s' % (pad, os.path.basename(full))) copy_dir((source[0], full), dest_full, vars, verbosity, simulate, indent=indent+1, use_cheetah=use_cheetah, sub_vars=sub_vars, interactive=interactive, svn_add=svn_add, template_renderer=template_renderer) continue elif not use_pkg_resources and os.path.isdir(full): if verbosity: print('%sRecursing into %s' % (pad, os.path.basename(full))) copy_dir(full, dest_full, vars, verbosity, simulate, indent=indent+1, use_cheetah=use_cheetah, sub_vars=sub_vars, interactive=interactive, svn_add=svn_add, template_renderer=template_renderer) continue elif use_pkg_resources: content = pkg_resources.resource_string(source[0], full) content = content.encode() else: with open(full, 'rb') as f: content = f.read() if sub_file: try: content = content.decode() content = substitute_content(content, vars, filename=full, use_cheetah=use_cheetah, template_renderer=template_renderer) content = content.encode() except SkipTemplate: continue if content is None: continue already_exists = os.path.exists(dest_full) if already_exists: with open(dest_full, 'rb') as f: old_content = f.read() if old_content == content: if verbosity: print('%s%s already exists (same content)' % (pad, dest_full)) continue if interactive: if not query_interactive( full, dest_full, content.decode(), old_content.decode(), simulate=simulate): continue elif not overwrite: continue if verbosity and use_pkg_resources: print('%sCopying %s to %s' % (pad, full, dest_full)) elif verbosity: print('%sCopying %s to %s' % (pad, os.path.basename(full), dest_full)) if not simulate: with open(dest_full, 'wb') as f: f.write(content) if svn_add and not already_exists: if os.system('svn info %r >/dev/null 2>&1' % os.path.dirname(os.path.abspath(dest_full))) > 0: if verbosity > 1: print('%sNot part of a svn working copy; cannot add file' % pad) else: cmd = ['svn', 'add', dest_full] if verbosity > 1: print('%sRunning: %s' % (pad, ' '.join(cmd))) if not simulate: # @@: Should if subprocess is None: raise RuntimeError('copydir failed, environment ' 'does not support subprocess ' 'module') proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) stdout, stderr = proc.communicate() if verbosity > 1 and stdout: print('Script output:') print(stdout) elif svn_add and already_exists and verbosity > 1: print('%sFile already exists (not doing svn add)' % pad) def should_skip_file(name): """ Checks if a file should be skipped based on its name. If it should be skipped, returns the reason, otherwise returns None. """ if name.startswith('.'): return 'Skipping hidden file %(filename)s' if name.endswith('~') or name.endswith('.bak'): return 'Skipping backup file %(filename)s' if name.endswith('.pyc') or name.endswith('.pyo'): return 'Skipping %s file %%(filename)s' % os.path.splitext(name)[1] if name.endswith('$py.class'): return 'Skipping $py.class file %(filename)s' if name in ('CVS', '_darcs'): return 'Skipping version control directory %(filename)s' return None # Overridden on user's request: all_answer = None def query_interactive(src_fn, dest_fn, src_content, dest_content, simulate): global all_answer from difflib import unified_diff, context_diff u_diff = list(unified_diff( dest_content.splitlines(), src_content.splitlines(), dest_fn, src_fn)) c_diff = list(context_diff( dest_content.splitlines(), src_content.splitlines(), dest_fn, src_fn)) added = len([l for l in u_diff if l.startswith('+') and not l.startswith('+++')]) removed = len([l for l in u_diff if l.startswith('-') and not l.startswith('---')]) if added > removed: msg = '; %i lines added' % (added-removed) elif removed > added: msg = '; %i lines removed' % (removed-added) else: msg = '' print('Replace %i bytes with %i bytes (%i/%i lines changed%s)' % ( len(dest_content), len(src_content), removed, len(dest_content.splitlines()), msg)) prompt = 'Overwrite %s [y/n/d/B/?] ' % dest_fn while 1: if all_answer is None: response = input(prompt).strip().lower() else: response = all_answer if not response or response[0] == 'b': import shutil new_dest_fn = dest_fn + '.bak' n = 0 while os.path.exists(new_dest_fn): n += 1 new_dest_fn = dest_fn + '.bak' + str(n) print('Backing up %s to %s' % (dest_fn, new_dest_fn)) if not simulate: shutil.copyfile(dest_fn, new_dest_fn) return True elif response.startswith('all '): rest = response[4:].strip() if not rest or rest[0] not in ('y', 'n', 'b'): print(query_usage) continue response = all_answer = rest[0] if response[0] == 'y': return True elif response[0] == 'n': return False elif response == 'dc': print('\n'.join(c_diff)) elif response[0] == 'd': print('\n'.join(u_diff)) else: print(query_usage) query_usage = """\ Responses: Y(es): Overwrite the file with the new content. N(o): Do not overwrite the file. D(iff): Show a unified diff of the proposed changes (dc=context diff) B(ackup): Save the current file contents to a .bak file (and overwrite) Type "all Y/N/B" to use Y/N/B for answer to all future questions """ def svn_makedirs(dir, svn_add, verbosity, pad): parent = os.path.dirname(os.path.abspath(dir)) if not os.path.exists(parent): svn_makedirs(parent, svn_add, verbosity, pad) os.mkdir(dir) if not svn_add: return if os.system('svn info %r >/dev/null 2>&1' % parent) > 0: if verbosity > 1: print('%sNot part of a svn working copy; cannot add directory' % pad) return cmd = ['svn', 'add', dir] if verbosity > 1: print('%sRunning: %s' % (pad, ' '.join(cmd))) proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) stdout, stderr = proc.communicate() if verbosity > 1 and stdout: print('Script output:') print(stdout) def substitute_filename(fn, vars): for var, value in vars.items(): fn = fn.replace('+%s+' % var, str(value)) return fn def substitute_content(content, vars, filename='', use_cheetah=False, template_renderer=None): global Cheetah v = standard_vars.copy() v.update(vars) vars = v if template_renderer is not None: return template_renderer(content, vars, filename=filename) if not use_cheetah: tmpl = LaxTemplate(content) try: return tmpl.substitute(TypeMapper(v)) except Exception as e: _add_except(e, ' in file %s' % filename) raise if Cheetah is None: import Cheetah.Template tmpl = Cheetah.Template.Template(source=content, searchList=[vars]) return careful_sub(tmpl, vars, filename) def careful_sub(cheetah_template, vars, filename): """ Substitutes the template with the variables, using the .body() method if it exists. It assumes that the variables were also passed in via the searchList. """ if not hasattr(cheetah_template, 'body'): return sub_catcher(filename, vars, str, cheetah_template) body = cheetah_template.body args, varargs, varkw, defaults = inspect.getargspec(body) call_vars = {} for arg in args: if arg in vars: call_vars[arg] = vars[arg] return sub_catcher(filename, vars, body, **call_vars) def sub_catcher(filename, vars, func, *args, **kw): """ Run a substitution, returning the value. If an error occurs, show the filename. If the error is a NameError, show the variables. """ try: return func(*args, **kw) except SkipTemplate as e: print('Skipping file %s' % filename) if str(e): print(str(e)) raise except Exception as e: print('Error in file %s:' % filename) if isinstance(e, NameError): for name, value in sorted(vars.items()): print('%s = %r' % (name, value)) raise def html_quote(s): if s is None: return '' return html.escape(str(s), 1) def url_quote(s): if s is None: return '' return quote(str(s)) def test(conf, true_cond, false_cond=None): if conf: return true_cond else: return false_cond def skip_template(condition=True, *args): """ Raise SkipTemplate, which causes copydir to skip the template being processed. If you pass in a condition, only raise if that condition is true (allows you to use this with string.Template) If you pass any additional arguments, they will be used to instantiate SkipTemplate (generally use like ``skip_template(license=='GPL', 'Skipping file; not using GPL')``) """ if condition: raise SkipTemplate(*args) def _add_except(exc, info): if not hasattr(exc, 'args') or exc.args is None: return args = list(exc.args) if args: args[0] += ' ' + info else: args = [info] exc.args = tuple(args) return standard_vars = { 'nothing': None, 'html_quote': html_quote, 'url_quote': url_quote, 'empty': '""', 'test': test, 'repr': repr, 'str': str, 'bool': bool, 'SkipTemplate': SkipTemplate, 'skip_template': skip_template, } class TypeMapper(dict): def __getitem__(self, item): options = item.split('|') for op in options[:-1]: try: value = eval_with_catch(op, dict(self.items())) break except (NameError, KeyError): pass else: value = eval(options[-1], dict(self.items())) if value is None: return '' else: return str(value) def eval_with_catch(expr, vars): try: return eval(expr, vars) except Exception as e: _add_except(e, 'in expression %r' % expr) raise class LaxTemplate(string.Template): # This change of pattern allows for anything in braces, but # only identifiers outside of braces: pattern = r""" \$(?: (?P\$) | # Escape sequence of two delimiters (?P[_a-z][_a-z0-9]*) | # delimiter and a Python identifier {(?P.*?)} | # delimiter and a braced identifier (?P) # Other ill-formed delimiter exprs ) """ pastescript-3.7.0/paste/script/create_distro.py000066400000000000000000000407271474421447600217320ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php import fnmatch import os import re import sys import pkg_resources from . import copydir from . import pluginlib from .command import Command, BadCommand class CreateDistroCommand(Command): usage = 'PACKAGE_NAME [VAR=VALUE VAR2=VALUE2 ...]' summary = "Create the file layout for a Python distribution" short_description = summary description = """\ Create a new project. Projects are typically Python packages, ready for distribution. Projects are created from templates, and represent different kinds of projects -- associated with a particular framework for instance. """ parser = Command.standard_parser( simulate=True, no_interactive=True, quiet=True, overwrite=True) parser.add_option('-t', '--template', dest='templates', metavar='TEMPLATE', action='append', help="Add a template to the create process") parser.add_option('-o', '--output-dir', dest='output_dir', metavar='DIR', default='.', help="Write put the directory into DIR (default current directory)") parser.add_option('--svn-repository', dest='svn_repository', metavar='REPOS', help="Create package at given repository location (this will create the standard trunk/ tags/ branches/ hierarchy)") parser.add_option('--list-templates', dest='list_templates', action='store_true', help="List all templates available") parser.add_option('--list-variables', dest="list_variables", action="store_true", help="List all variables expected by the given template (does not create a package)") parser.add_option('--inspect-files', dest='inspect_files', action='store_true', help="Show where the files in the given (already created) directory came from (useful when using multiple templates)") parser.add_option('--config', action='store', dest='config', help="Template variables file") _bad_chars_re = re.compile('[^a-zA-Z0-9_]') default_verbosity = 1 default_interactive = 1 def command(self): if self.options.list_templates: return self.list_templates() asked_tmpls = self.options.templates or ['basic_package'] templates = [] for tmpl_name in asked_tmpls: self.extend_templates(templates, tmpl_name) if self.options.list_variables: return self.list_variables(templates) if self.verbose: print('Selected and implied templates:') max_tmpl_name = max([len(tmpl_name) for tmpl_name, tmpl in templates]) for tmpl_name, tmpl in templates: print(' %s%s %s' % ( tmpl_name, ' '*(max_tmpl_name-len(tmpl_name)), tmpl.summary)) print() if not self.args: if self.interactive: dist_name = self.challenge('Enter project name') else: raise BadCommand('You must provide a PACKAGE_NAME') else: dist_name = self.args[0].lstrip(os.path.sep) templates = [tmpl for name, tmpl in templates] output_dir = os.path.join(self.options.output_dir, dist_name) pkg_name = self._bad_chars_re.sub('', dist_name.lower()) vars = {'project': dist_name, 'package': pkg_name, 'egg': pluginlib.egg_name(dist_name), } vars.update(self.parse_vars(self.args[1:])) if self.options.config and os.path.exists(self.options.config): for key, value in self.read_vars(self.options.config).items(): vars.setdefault(key, value) if self.verbose: # @@: > 1? self.display_vars(vars) if self.options.inspect_files: self.inspect_files( output_dir, templates, vars) return if not os.path.exists(output_dir): # We want to avoid asking questions in copydir if the path # doesn't exist yet copydir.all_answer = 'y' if self.options.svn_repository: self.setup_svn_repository(output_dir, dist_name) # First we want to make sure all the templates get a chance to # set their variables, all at once, with the most specialized # template going first (the last template is the most # specialized)... for template in templates[::-1]: vars = template.check_vars(vars, self) # Gather all the templates egg_plugins into one var egg_plugins = set() for template in templates: egg_plugins.update(template.egg_plugins) egg_plugins = sorted(egg_plugins) vars['egg_plugins'] = egg_plugins for template in templates: self.create_template( template, output_dir, vars) found_setup_py = False paster_plugins_mtime = None if os.path.exists(os.path.join(output_dir, 'setup.py')): # Grab paster_plugins.txt's mtime; used to determine if the # egg_info command wrote to it try: egg_info_dir = pluginlib.egg_info_dir(output_dir, dist_name) except IOError: egg_info_dir = None if egg_info_dir is not None: plugins_path = os.path.join(egg_info_dir, 'paster_plugins.txt') if os.path.exists(plugins_path): paster_plugins_mtime = os.path.getmtime(plugins_path) self.run_command(sys.executable, 'setup.py', 'egg_info', cwd=output_dir, # This shouldn't be necessary, but a bug in setuptools 0.6c3 is causing a (not entirely fatal) problem that I don't want to fix right now: expect_returncode=True) found_setup_py = True elif self.verbose > 1: print('No setup.py (cannot run egg_info)') package_dir = vars.get('package_dir', None) if package_dir: output_dir = os.path.join(output_dir, package_dir) # With no setup.py this doesn't make sense: if found_setup_py: # Only write paster_plugins.txt if it wasn't written by # egg_info (the correct way). leaving us to do it is # deprecated and you'll get warned egg_info_dir = pluginlib.egg_info_dir(output_dir, dist_name) plugins_path = os.path.join(egg_info_dir, 'paster_plugins.txt') if len(egg_plugins) and (not os.path.exists(plugins_path) or \ os.path.getmtime(plugins_path) == paster_plugins_mtime): if self.verbose: print(('Manually creating paster_plugins.txt (deprecated! ' 'pass a paster_plugins keyword to setup() instead)'), file=sys.stderr) for plugin in egg_plugins: if self.verbose: print('Adding %s to paster_plugins.txt' % plugin) if not self.simulate: pluginlib.add_plugin(egg_info_dir, plugin) if self.options.svn_repository: self.add_svn_repository(vars, output_dir) if self.options.config: write_vars = vars.copy() del write_vars['project'] del write_vars['package'] self.write_vars(self.options.config, write_vars) def create_template(self, template, output_dir, vars): if self.verbose: print('Creating template %s' % template.name) template.run(self, output_dir, vars) def setup_svn_repository(self, output_dir, dist_name): # @@: Use subprocess svn_repos = self.options.svn_repository svn_repos_path = os.path.join(svn_repos, dist_name).replace('\\','/') svn_command = 'svn' if sys.platform == 'win32': svn_command += '.exe' # @@: The previous method of formatting this string using \ doesn't work on Windows cmd = '%(svn_command)s mkdir %(svn_repos_path)s' + \ ' %(svn_repos_path)s/trunk %(svn_repos_path)s/tags' + \ ' %(svn_repos_path)s/branches -m "New project %(dist_name)s"' cmd = cmd % { 'svn_repos_path': svn_repos_path, 'dist_name': dist_name, 'svn_command':svn_command, } if self.verbose: print("Running:") print(cmd) if not self.simulate: os.system(cmd) svn_repos_path_trunk = os.path.join(svn_repos_path,'trunk').replace('\\','/') cmd = svn_command+' co "%s" "%s"' % (svn_repos_path_trunk, output_dir) if self.verbose: print("Running %s" % cmd) if not self.simulate: os.system(cmd) ignore_egg_info_files = [ 'top_level.txt', 'entry_points.txt', 'requires.txt', 'PKG-INFO', 'namespace_packages.txt', 'SOURCES.txt', 'dependency_links.txt', 'not-zip-safe'] def add_svn_repository(self, vars, output_dir): svn_repos = self.options.svn_repository egg_info_dir = pluginlib.egg_info_dir(output_dir, vars['project']) svn_command = 'svn' if sys.platform == 'win32': svn_command += '.exe' self.run_command(svn_command, 'add', '-N', egg_info_dir) paster_plugins_file = os.path.join( egg_info_dir, 'paster_plugins.txt') if os.path.exists(paster_plugins_file): self.run_command(svn_command, 'add', paster_plugins_file) self.run_command(svn_command, 'ps', 'svn:ignore', '\n'.join(self.ignore_egg_info_files), egg_info_dir) if self.verbose: print ("You must next run 'svn commit' to commit the " "files to repository") def extend_templates(self, templates, tmpl_name): if '#' in tmpl_name: dist_name, tmpl_name = tmpl_name.split('#', 1) else: dist_name, tmpl_name = None, tmpl_name if dist_name is None: for entry in self.all_entry_points(): if entry.name == tmpl_name: tmpl = entry.load()(entry.name) dist_name = entry.dist.project_name break else: raise LookupError( 'Template by name %r not found' % tmpl_name) else: dist = pkg_resources.get_distribution(dist_name) entry = dist.get_entry_info( 'paste.paster_create_template', tmpl_name) tmpl = entry.load()(entry.name) full_name = '%s#%s' % (dist_name, tmpl_name) for item_full_name, item_tmpl in templates: if item_full_name == full_name: # Already loaded return for req_name in tmpl.required_templates: self.extend_templates(templates, req_name) templates.append((full_name, tmpl)) def all_entry_points(self): if not hasattr(self, '_entry_points'): self._entry_points = list(pkg_resources.iter_entry_points( 'paste.paster_create_template')) return self._entry_points def display_vars(self, vars): vars = sorted(vars.items()) print('Variables:') max_var = max([len(n) for n, v in vars]) for name, value in vars: print(' %s:%s %s' % ( name, ' '*(max_var-len(name)), value)) def list_templates(self): templates = [] for entry in self.all_entry_points(): try: templates.append(entry.load()(entry.name)) except Exception as e: # We will not be stopped! print('Warning: could not load entry point %s (%s: %s)' % ( entry.name, e.__class__.__name__, e)) max_name = max([len(t.name) for t in templates]) templates = sorted(templates, key=lambda a: a.name) print('Available templates:') for template in templates: # @@: Wrap description print(' %s:%s %s' % ( template.name, ' '*(max_name-len(template.name)), template.summary)) def inspect_files(self, output_dir, templates, vars): file_sources = {} for template in templates: self._find_files(template, vars, file_sources) self._show_files(output_dir, file_sources) self._show_leftovers(output_dir, file_sources) def _find_files(self, template, vars, file_sources): tmpl_dir = template.template_dir() self._find_template_files( template, tmpl_dir, vars, file_sources) def _find_template_files(self, template, tmpl_dir, vars, file_sources, join=''): full_dir = os.path.join(tmpl_dir, join) for name in os.listdir(full_dir): if name.startswith('.'): continue if os.path.isdir(os.path.join(full_dir, name)): self._find_template_files( template, tmpl_dir, vars, file_sources, join=os.path.join(join, name)) continue partial = os.path.join(join, name) for name, value in vars.items(): partial = partial.replace('+%s+' % name, value) if partial.endswith('_tmpl'): partial = partial[:-5] file_sources.setdefault(partial, []).append(template) _ignore_filenames = ['.*', '*.pyc', '*.bak*'] _ignore_dirs = ['CVS', '_darcs', '.svn'] def _show_files(self, output_dir, file_sources, join='', indent=0): pad = ' '*(2*indent) full_dir = os.path.join(output_dir, join) names = os.listdir(full_dir) dirs = [n for n in names if os.path.isdir(os.path.join(full_dir, n))] fns = [n for n in names if not os.path.isdir(os.path.join(full_dir, n))] dirs.sort() names.sort() for name in names: skip_this = False for ext in self._ignore_filenames: if fnmatch.fnmatch(name, ext): if self.verbose > 1: print('%sIgnoring %s' % (pad, name)) skip_this = True break if skip_this: continue partial = os.path.join(join, name) if partial not in file_sources: if self.verbose > 1: print('%s%s (not from template)' % (pad, name)) continue templates = file_sources.pop(partial) print('%s%s from:' % (pad, name)) for template in templates: print('%s %s' % (pad, template.name)) for dir in dirs: if dir in self._ignore_dirs: continue print('%sRecursing into %s/' % (pad, dir)) self._show_files( output_dir, file_sources, join=os.path.join(join, dir), indent=indent+1) def _show_leftovers(self, output_dir, file_sources): if not file_sources: return print() print('These files were supposed to be generated by templates') print('but were not found:') file_sources = sorted(file_sources.items()) for partial, templates in file_sources: print(' %s from:' % partial) for template in templates: print(' %s' % template.name) def list_variables(self, templates): for tmpl_name, tmpl in templates: if not tmpl.read_vars(): if self.verbose > 1: self._show_template_vars( tmpl_name, tmpl, 'No variables found') continue self._show_template_vars(tmpl_name, tmpl) def _show_template_vars(self, tmpl_name, tmpl, message=None): title = '%s (from %s)' % (tmpl.name, tmpl_name) print(title) print('-'*len(title)) if message is not None: print(' %s' % message) print() return tmpl.print_vars(indent=2) pastescript-3.7.0/paste/script/default_sysconfig.py000066400000000000000000000026101474421447600226000ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php """ This module contains default sysconfig settings. The command object is inserted into this module as a global variable ``paste_command``, and can be used inside functions. """ def add_custom_options(parser): """ This method can modify the ``parser`` object (which is an ``optparse.OptionParser`` instance). This can be used to add new options to the command. """ pass def default_config_filename(installer): """ This function can return a default filename or directory for the configuration file, if none was explicitly given. Return None to mean no preference. The first non-None returning value will be used. Pay attention to ``installer.expect_config_directory`` here, and to ``installer.default_config_filename``. """ return installer.default_config_filename def install_variables(installer): """ Returns a dictionary of variables for use later in the process (e.g., filling a configuration file). These are combined from all sysconfig files. """ return {} def post_setup_hook(installer, config_file): """ This is called at the very end of ``paster setup-app``. You might use it to register an application globally. """ pass pastescript-3.7.0/paste/script/entrypoints.py000066400000000000000000000221451474421447600214730ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php import textwrap import os import pkg_resources from .command import Command, BadCommand import fnmatch import re import traceback from io import StringIO import inspect import types class EntryPointCommand(Command): usage = "ENTRY_POINT" summary = "Show information about entry points" description = """\ Shows information about one or many entry points (you can use wildcards for entry point names). Entry points are used for Egg plugins, and are named resources -- like an application, template plugin, or other resource. Entry points have a [group] which defines what kind of object they describe, and inside groups each entry point is named. """ max_args = 2 parser = Command.standard_parser(verbose=False) parser.add_option('--list', '-l', dest='list_entry_points', action='store_true', help='List all the kinds of entry points on the system') parser.add_option('--egg', '-e', dest='show_egg', help="Show all the entry points for the given Egg") parser.add_option('--regex', dest='use_regex', action='store_true', help="Make pattern match as regular expression, not just a wildcard pattern") def command(self): if self.options.list_entry_points: return self.list_entry_points() if self.options.show_egg: return self.show_egg(self.options.show_egg) if not self.args: raise BadCommand("You must give an entry point (or --list)") pattern = self.get_pattern(self.args[0]) groups = self.get_groups_by_pattern(pattern) if not groups: raise BadCommand('No group matched %s' % self.args[0]) ep_pat = None if len(self.args) > 1: ep_pat = self.get_pattern(self.args[1]) for group in groups: desc = self.get_group_description(group) print('[%s]' % group) if desc: print(self.wrap(desc)) print() self.print_entry_points_by_group(group, ep_pat) def print_entry_points_by_group(self, group, ep_pat): env = pkg_resources.Environment() project_names = sorted(env) for project_name in project_names: dists = list(env[project_name]) assert dists dist = dists[0] entries = list(dist.get_entry_map(group).values()) if ep_pat: entries = [e for e in entries if ep_pat.search(e.name)] if not entries: continue if len(dists) > 1: print('%s (+ %i older versions)' % ( dist, len(dists)-1)) else: print('%s' % dist) entries.sort(key=lambda entry: entry.name) for entry in entries: print(self._ep_description(entry)) desc = self.get_entry_point_description(entry, group) if desc and desc.description: print(self.wrap(desc.description, indent=4)) def show_egg(self, egg_name): group_pat = None if self.args: group_pat = self.get_pattern(self.args[0]) ep_pat = None if len(self.args) > 1: ep_pat = self.get_pattern(self.args[1]) if egg_name.startswith('egg:'): egg_name = egg_name[4:] dist = pkg_resources.get_distribution(egg_name) entry_map = dist.get_entry_map() entry_groups = sorted(entry_map.items()) for group, points in entry_groups: if group_pat and not group_pat.search(group): continue print('[%s]' % group) points = sorted(points.items()) for name, entry in points: if ep_pat: if not ep_pat.search(name): continue print(self._ep_description(entry)) desc = self.get_entry_point_description(entry, group) if desc and desc.description: print(self.wrap(desc.description, indent=2)) print() def wrap(self, text, indent=0): text = dedent(text) width = int(os.environ.get('COLUMNS', 70)) - indent text = '\n'.join([line.rstrip() for line in text.splitlines()]) paras = text.split('\n\n') new_paras = [] for para in paras: if para.lstrip() == para: # leading whitespace means don't rewrap para = '\n'.join(textwrap.wrap(para, width)) new_paras.append(para) text = '\n\n'.join(new_paras) lines = [' '*indent + line for line in text.splitlines()] return '\n'.join(lines) def _ep_description(self, ep, pad_name=None): name = ep.name if pad_name is not None: name = name + ' '*(pad_name-len(name)) dest = ep.module_name if ep.attrs: dest = dest + ':' + '.'.join(ep.attrs) return '%s = %s' % (name, dest) def get_pattern(self, s): if not s: return None if self.options.use_regex: return re.compile(s) else: return re.compile(fnmatch.translate(s), re.I) def list_entry_points(self): pattern = self.get_pattern(self.args and self.args[0]) groups = self.get_groups_by_pattern(pattern) print('%i entry point groups found:' % len(groups)) for group in groups: desc = self.get_group_description(group) print('[%s]' % group) if desc: if hasattr(desc, 'description'): desc = desc.description print(self.wrap(desc, indent=2)) def get_groups_by_pattern(self, pattern): env = pkg_resources.Environment() eps = {} for project_name in env: for dist in env[project_name]: for name in pkg_resources.get_entry_map(dist): if pattern and not pattern.search(name): continue if (not pattern and name.startswith('paste.description.')): continue eps[name] = None return sorted(eps.keys()) def get_group_description(self, group): for entry in pkg_resources.iter_entry_points('paste.entry_point_description'): if entry.name == group: ep = entry.load() if hasattr(ep, 'description'): return ep.description else: return ep return None def get_entry_point_description(self, ep, group): try: return self._safe_get_entry_point_description(ep, group) except Exception as e: out = StringIO() traceback.print_exc(file=out) return ErrorDescription(e, out.getvalue()) def _safe_get_entry_point_description(self, ep, group): ep.dist.activate() meta_group = 'paste.description.'+group meta = ep.dist.get_entry_info(meta_group, ep.name) if not meta: generic = list(pkg_resources.iter_entry_points( meta_group, 'generic')) if not generic: return super_generic(ep.load()) # @@: Error if len(generic) > 1? obj = generic[0].load() desc = obj(ep, group) else: desc = meta.load() return desc class EntryPointDescription(object): def __init__(self, group): self.group = group # Should define: # * description class SuperGeneric(object): def __init__(self, doc_object): self.doc_object = doc_object self.description = dedent(self.doc_object.__doc__) try: if isinstance(self.doc_object, type): func = self.doc_object.__init__ elif (hasattr(self.doc_object, '__call__') and not isinstance(self.doc_object, types.FunctionType)): func = self.doc_object.__call__ else: func = self.doc_object if hasattr(func, '__paste_sig__'): sig = func.__paste_sig__ else: sig = str(inspect.signature(func)) except TypeError: sig = None if sig: if self.description: self.description = '%s\n\n%s' % ( sig, self.description) else: self.description = sig def dedent(s): if s is None: return s s = s.strip('\n').strip('\r') return textwrap.dedent(s) def super_generic(obj): desc = SuperGeneric(obj) if not desc.description: return None return desc class ErrorDescription(object): def __init__(self, exc, tb): self.exc = exc self.tb = '\n'.join(tb) self.description = 'Error loading: %s' % exc pastescript-3.7.0/paste/script/epdesc.py000066400000000000000000000030741474421447600203400ustar00rootroot00000000000000class MetaEntryPointDescription(object): description = """ This is an entry point that describes other entry points. """ class CreateTemplateDescription(object): description = """ Entry point for creating the file layout for a new project from a template. """ class PasterCommandDescription(object): description = """ Entry point that adds a command to the ``paster`` script to a project that has specifically enabled the command. """ class GlobalPasterCommandDescription(object): description = """ Entry point that adds a command to the ``paster`` script globally. """ class AppInstallDescription(object): description = """ This defines a runner that can install the application given a configuration file. """ ################################################## ## Not in Paste per se, but we'll document ## them... class ConsoleScriptsDescription(object): description = """ When a package is installed, any entry point listed here will be turned into a command-line script. """ class DistutilsCommandsDescription(object): description = """ This will add a new command when running ``python setup.py entry-point-name`` if the package uses setuptools. """ class SetupKeywordsDescription(object): description = """ This adds a new keyword to setup.py's setup() function, and a validator to validate the value. """ class EggInfoWriters(object): description = """ This adds a new writer that creates files in the PkgName.egg-info/ directory. """ pastescript-3.7.0/paste/script/exe.py000066400000000000000000000065761474421447600176700ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php import re import os import sys import shlex import pkg_resources from . import command class ExeCommand(command.Command): parser = command.Command.standard_parser(verbose=False) summary = "Run #! executable files" description = """\ Use this at the top of files like: #!/usr/bin/env /path/to/paster exe subcommand The rest of the file will be used as a config file for the given command, if it wants a config file. You can also include an [exe] section in the file, which looks like: [exe] command = serve log_file = /path/to/log add = /path/to/other/config.ini Which translates to: paster serve --log-file=/path/to/log /path/to/other/config.ini """ hidden = True _exe_section_re = re.compile(r'^\s*\[\s*exe\s*\]\s*$') _section_re = re.compile(r'^\s*\[') def run(self, argv): if argv and argv[0] in ('-h', '--help'): print(self.description) return if os.environ.get('REQUEST_METHOD'): # We're probably in a CGI environment sys.stdout = sys.stderr os.environ['PASTE_DEFAULT_QUIET'] = 'true' # Maybe import cgitb or something? if '_' not in os.environ: print("Warning: this command is intended to be run with a #! like:") print(" #!/usr/bin/env paster exe") print("It only works with /usr/bin/env, and only as a #! line.") # Should I actually shlex.split the args? filename = argv[-1] args = argv[:-1] extra_args = [] else: filename = os.environ['_'] extra_args = argv[:] args = [] while extra_args: if extra_args[0] == filename: extra_args.pop(0) break args.append(extra_args.pop(0)) vars = {'here': os.path.dirname(filename), '__file__': filename} f = open(filename) lines = f.readlines() f.close() options = {} lineno = 1 while lines: if self._exe_section_re.search(lines[0]): lines.pop(0) break lines.pop(0) lineno += 1 options = args for line in lines: lineno += 1 line = line.strip() if not line or line.startswith('#'): continue if self._section_re.search(line): break if '=' not in line: raise command.BadCommand('Missing = in %s at %s: %r' % (filename, lineno, line)) name, value = line.split('=', 1) name = name.strip() value = value.strip() if name == 'require': pkg_resources.require(value) elif name == 'command' or name == 'add': options.extend(shlex.split(value)) elif name == 'plugin': options[:0] = ['--plugin', value] else: value = value % vars options.append('--%s=%s' % (name.replace('_', '-'), value)) os.environ['PASTE_CONFIG_FILE'] = filename options.extend(extra_args) command.run(options) pastescript-3.7.0/paste/script/filemaker.py000066400000000000000000000323171474421447600210360ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php import os import pkg_resources from paste.script import pluginlib, copydir from paste.script.command import BadCommand difflib = None import subprocess class FileOp(object): """ Enhance the ease of file copying/processing from a package into a target project """ def __init__(self, simulate=False, verbose=True, interactive=True, source_dir=None, template_vars=None): """ Initialize our File operation helper object source_dir Should refer to the directory within the package that contains the templates to be used for the other copy operations. It is assumed that packages will keep all their templates under a hierarchy starting here. This should be an absolute path passed in, for example:: FileOp(source_dir=os.path.dirname(__file__) + '/templates') """ self.simulate = simulate self.verbose = verbose self.interactive = interactive if template_vars is None: template_vars = {} self.template_vars = template_vars self.source_dir = source_dir self.use_pkg_resources = isinstance(source_dir, tuple) def copy_file(self, template, dest, filename=None, add_py=True, package=True, template_renderer=None): """ Copy a file from the source location to somewhere in the destination. template The filename underneath self.source_dir to copy/process dest The destination directory in the project relative to where this command is being run filename What to name the file in the target project, use the same name as the template if not provided add_py Add a .py extension to all files copied package Whether or not this file is part of a Python package, and any directories created should contain a __init__.py file as well. template_renderer An optional template renderer """ if not filename: filename = template.split('/')[0] if filename.endswith('_tmpl'): filename = filename[:-5] base_package, cdir = self.find_dir(dest, package) self.template_vars['base_package'] = base_package content = self.load_content(base_package, cdir, filename, template, template_renderer=template_renderer) if add_py: # @@: Why is it a default to add a .py extension? filename = '%s.py' % filename dest = os.path.join(cdir, filename) self.ensure_file(dest, content, package) def copy_dir(self, template_dir, dest, destname=None, package=True): """ Copy a directory recursively, processing any files within it that need to be processed (end in _tmpl). template_dir Directory under self.source_dir to copy/process dest Destination directory into which this directory will be copied to. destname Use this name instead of the original template_dir name for creating the directory package This directory will be a Python package and needs to have a __init__.py file. """ # @@: This should actually be implemented raise NotImplementedError def load_content(self, base_package, base, name, template, template_renderer=None): blank = os.path.join(base, name + '.py') read_content = True if not os.path.exists(blank): if self.use_pkg_resources: fullpath = '/'.join([self.source_dir[1], template]) content = pkg_resources.resource_string( self.source_dir[0], fullpath) read_content = False blank = fullpath else: blank = os.path.join(self.source_dir, template) if read_content: f = open(blank, 'r') content = f.read() f.close() if blank.endswith('_tmpl'): content = copydir.substitute_content( content, self.template_vars, filename=blank, template_renderer=template_renderer) return content def find_dir(self, dirname, package=False): egg_info = pluginlib.find_egg_info_dir(os.getcwd()) # @@: Should give error about egg_info when top_level.txt missing f = open(os.path.join(egg_info, 'top_level.txt')) packages = [l.strip() for l in f.readlines() if l.strip() and not l.strip().startswith('#')] f.close() if not len(packages): raise BadCommand("No top level dir found for %s" % dirname) # @@: This doesn't support deeper servlet directories, # or packages not kept at the top level. base = os.path.dirname(egg_info) possible = [] for pkg in packages: d = os.path.join(base, pkg, dirname) if os.path.exists(d): possible.append((pkg, d)) if not possible: self.ensure_dir(os.path.join(base, packages[0], dirname), package=package) return self.find_dir(dirname) if len(possible) > 1: raise BadCommand( "Multiple %s dirs found (%s)" % (dirname, possible)) return possible[0] def parse_path_name_args(self, name): """ Given the name, assume that the first argument is a path/filename combination. Return the name and dir of this. If the name ends with '.py' that will be erased. Examples: comments -> comments, '' admin/comments -> comments, 'admin' h/ab/fred -> fred, 'h/ab' """ if name.endswith('.py'): # Erase extensions name = name[:-3] if '.' in name: # Turn into directory name: name = name.replace('.', os.path.sep) if '/' != os.path.sep: name = name.replace('/', os.path.sep) parts = name.split(os.path.sep) name = parts[-1] if not parts[:-1]: dir = '' elif len(parts[:-1]) == 1: dir = parts[0] else: dir = os.path.join(*parts[:-1]) return name, dir def ensure_dir(self, dir, svn_add=True, package=False): """ Ensure that the directory exists, creating it if necessary. Respects verbosity and simulation. Adds directory to subversion if ``.svn/`` directory exists in parent, and directory was created. package If package is True, any directories created will contain a __init__.py file. """ dir = dir.rstrip(os.sep) if not dir: # we either reached the parent-most directory, or we got # a relative directory # @@: Should we make sure we resolve relative directories # first? Though presumably the current directory always # exists. return if not os.path.exists(dir): self.ensure_dir(os.path.dirname(dir), svn_add=svn_add, package=package) if self.verbose: print('Creating %s' % self.shorten(dir)) if not self.simulate: os.mkdir(dir) if (svn_add and os.path.exists(os.path.join(os.path.dirname(dir), '.svn'))): self.svn_command('add', dir) if package: initfile = os.path.join(dir, '__init__.py') f = open(initfile, 'wb') f.write("#\n") f.close() print('Creating %s' % self.shorten(initfile)) if (svn_add and os.path.exists(os.path.join(os.path.dirname(dir), '.svn'))): self.svn_command('add', initfile) else: if self.verbose > 1: print("Directory already exists: %s" % self.shorten(dir)) def ensure_file(self, filename, content, svn_add=True, package=False): """ Ensure a file named ``filename`` exists with the given content. If ``--interactive`` has been enabled, this will ask the user what to do if a file exists with different content. """ global difflib self.ensure_dir(os.path.dirname(filename), svn_add=svn_add, package=package) if not os.path.exists(filename): if self.verbose: print('Creating %s' % filename) if not self.simulate: f = open(filename, 'wb') f.write(content) f.close() if svn_add and os.path.exists(os.path.join(os.path.dirname(filename), '.svn')): self.svn_command('add', filename) return f = open(filename, 'rb') old_content = f.read() f.close() if content == old_content: if self.verbose > 1: print('File %s matches expected content' % filename) return if self.interactive: print('Warning: file %s does not match expected content' % filename) if difflib is None: import difflib diff = difflib.context_diff( content.splitlines(), old_content.splitlines(), 'expected ' + filename, filename) print('\n'.join(diff)) if self.interactive: while 1: s = input( 'Overwrite file with new content? [y/N] ').strip().lower() if not s: s = 'n' if s.startswith('y'): break if s.startswith('n'): return print('Unknown response; Y or N please') else: return if self.verbose: print('Overwriting %s with new content' % filename) if not self.simulate: f = open(filename, 'wb') f.write(content) f.close() def shorten(self, fn, *paths): """ Return a shorted form of the filename (relative to the current directory), typically for displaying in messages. If ``*paths`` are present, then use os.path.join to create the full filename before shortening. """ if paths: fn = os.path.join(fn, *paths) if fn.startswith(os.getcwd()): return fn[len(os.getcwd()):].lstrip(os.path.sep) else: return fn _svn_failed = False def svn_command(self, *args, **kw): """ Run an svn command, but don't raise an exception if it fails. """ try: return self.run_command('svn', *args, **kw) except OSError as e: if not self._svn_failed: print('Unable to run svn command (%s); proceeding anyway' % e) self._svn_failed = True def run_command(self, cmd, *args, **kw): """ Runs the command, respecting verbosity and simulation. Returns stdout, or None if simulating. """ cwd = popdefault(kw, 'cwd', os.getcwd()) capture_stderr = popdefault(kw, 'capture_stderr', False) expect_returncode = popdefault(kw, 'expect_returncode', False) assert not kw, ("Arguments not expected: %s" % kw) if capture_stderr: stderr_pipe = subprocess.STDOUT else: stderr_pipe = subprocess.PIPE try: proc = subprocess.Popen([cmd] + list(args), cwd=cwd, stderr=stderr_pipe, stdout=subprocess.PIPE) except OSError as e: if e.errno != 2: # File not found raise raise OSError( "The expected executable %s was not found (%s)" % (cmd, e)) if self.verbose: print('Running %s %s' % (cmd, ' '.join(args))) if self.simulate: return None stdout, stderr = proc.communicate() if proc.returncode and not expect_returncode: if not self.verbose: print('Running %s %s' % (cmd, ' '.join(args))) print('Error (exit code: %s)' % proc.returncode) if stderr: print(stderr) raise OSError("Error executing command %s" % cmd) if self.verbose > 2: if stderr: print('Command error output:') print(stderr) if stdout: print('Command output:') print(stdout) return stdout def popdefault(dict, name, default=None): if name not in dict: return default else: v = dict[name] del dict[name] return v pastescript-3.7.0/paste/script/flup_server.py000066400000000000000000000071451474421447600214340ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php from paste.deploy.converters import aslist, asbool from paste.script.serve import ensure_port_cleanup import warnings def warn(name, stacklevel=3): # Deprecated 2007-12-17 warnings.warn( 'The egg:PasteScript#flup_%s entry point is deprecated; please use egg:Flup#%s instead' % (name, name), DeprecationWarning, stacklevel=stacklevel) def run_ajp_thread(wsgi_app, global_conf, scriptName='', host='localhost', port='8009', allowedServers='127.0.0.1'): import flup.server.ajp warn('ajp_thread') addr = (host, int(port)) ensure_port_cleanup([addr]) s = flup.server.ajp.WSGIServer( wsgi_app, scriptName=scriptName, bindAddress=addr, allowedServers=aslist(allowedServers), ) s.run() def run_ajp_fork(wsgi_app, global_conf, scriptName='', host='localhost', port='8009', allowedServers='127.0.0.1'): import flup.server.ajp_fork warn('ajp_fork') addr = (host, int(port)) ensure_port_cleanup([addr]) s = flup.server.ajp_fork.WSGIServer( wsgi_app, scriptName=scriptName, bindAddress=addr, allowedServers=aslist(allowedServers), ) s.run() def run_fcgi_thread(wsgi_app, global_conf, host=None, port=None, socket=None, umask=None, multiplexed=False): import flup.server.fcgi warn('fcgi_thread') if socket: assert host is None and port is None sock = socket elif host: assert host is not None and port is not None sock = (host, int(port)) ensure_port_cleanup([sock]) else: sock = None if umask is not None: umask = int(umask) s = flup.server.fcgi.WSGIServer( wsgi_app, bindAddress=sock, umask=umask, multiplexed=asbool(multiplexed)) s.run() def run_fcgi_fork(wsgi_app, global_conf, host=None, port=None, socket=None, umask=None, multiplexed=False): import flup.server.fcgi_fork warn('fcgi_fork') if socket: assert host is None and port is None sock = socket elif host: assert host is not None and port is not None sock = (host, int(port)) ensure_port_cleanup([sock]) else: sock = None if umask is not None: umask = int(umask) s = flup.server.fcgi_fork.WSGIServer( wsgi_app, bindAddress=sock, umask=umask, multiplexed=asbool(multiplexed)) s.run() def run_scgi_thread(wsgi_app, global_conf, scriptName='', host='localhost', port='4000', allowedServers='127.0.0.1'): import flup.server.scgi warn('scgi_thread') addr = (host, int(port)) ensure_port_cleanup([addr]) s = flup.server.scgi.WSGIServer( wsgi_app, scriptName=scriptName, bindAddress=addr, allowedServers=aslist(allowedServers), ) s.run() def run_scgi_fork(wsgi_app, global_conf, scriptName='', host='localhost', port='4000', allowedServers='127.0.0.1'): import flup.server.scgi_fork warn('scgi_fork') addr = (host, int(port)) ensure_port_cleanup([addr]) s = flup.server.scgi_fork.WSGIServer( wsgi_app, scriptName=scriptName, bindAddress=addr, allowedServers=aslist(allowedServers), ) s.run() pastescript-3.7.0/paste/script/grep.py000066400000000000000000000141001474421447600200220ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php import os import py_compile import marshal import inspect import re import tokenize from .command import Command from . import pluginlib class GrepCommand(Command): summary = 'Search project for symbol' usage = 'SYMBOL' max_args = 1 min_args = 1 bad_names = ['.svn', 'CVS', '_darcs'] parser = Command.standard_parser() parser.add_option( '-x', '--exclude-module', metavar="module.name", dest="exclude_modules", action="append", help="Don't search the given module") parser.add_option( '-t', '--add-type', metavar=".ext", dest="add_types", action="append", help="Search the given type of files") def command(self): self.exclude_modules = self.options.exclude_modules or [] self.add_types = self.options.add_types or [] self.symbol = self.args[0] self.basedir = os.path.dirname( pluginlib.find_egg_info_dir(os.getcwd())) if self.verbose: print("Searching in %s" % self.basedir) self.total_files = 0 self.search_dir(self.basedir) if self.verbose > 1: print("Searched %i files" % self.total_files) def search_dir(self, dir): names = os.listdir(dir) names.sort() dirs = [] for name in names: full = os.path.join(dir, name) if name in self.bad_names: continue if os.path.isdir(full): # Breadth-first; we'll do this later... dirs.append(full) continue for t in self.add_types: if name.lower().endswith(t.lower()): self.search_text(full) if not name.endswith('.py'): continue self.search_file(full) for dir in dirs: self.search_dir(dir) def search_file(self, filename): self.total_files += 1 if not filename.endswith('.py'): self.search_text(filename) return pyc = filename[:-2]+'pyc' if not os.path.exists(pyc): try: py_compile.compile(filename) except OSError: # ignore permission error if the .pyc cannot be written pass if not os.path.exists(pyc): # Invalid syntax... self.search_text(filename, as_module=True) return with open(pyc, 'rb') as f: # .pyc Header: f.read(8) try: code = marshal.load(f) except ValueError: # Fail to load the byteload. For example, Python 3.4 cannot # load Python 2.7 bytecode. pass else: self.search_code(code, filename, []) def search_code(self, code, filename, path): if code.co_name != "?": path = path + [code.co_name] else: path = path sym = self.symbol if sym in code.co_varnames: self.found(code, filename, path) elif sym in code.co_names: self.found(code, filename, path) for const in code.co_consts: if const == sym: self.found(code, filename, path) if inspect.iscode(const): if not const.co_filename == filename: continue self.search_code(const, filename, path) def _open(self, filename): if filename.endswith('.py') and hasattr(tokenize, 'open'): # On Python 3.2 and newer, open Python files with tokenize.open(). # This functions uses the encoding cookie to get the encoding. return tokenize.open(filename) else: return open(filename) def search_text(self, filename, as_module=False): with self._open(filename) as f: lineno = 0 any = False for line in f: lineno += 1 if line.find(self.symbol) != -1: if not any: any = True if as_module: print('%s (unloadable)' % self.module_name(filename)) else: print(self.relative_name(filename)) print(' %3i %s' % (lineno, line)) if not self.verbose: break def found(self, code, filename, path): print(self.display(filename, path)) self.find_occurance(code) def find_occurance(self, code): with self._open(code.co_filename) as f: lineno = 0 for index, line in zip(range(code.co_firstlineno), f): lineno += 1 pass first_indent = None for line in f: lineno += 1 if line.find(self.symbol) != -1: this_indent = len(re.match(r'^[ \t]*', line).group(0)) if first_indent is None: first_indent = this_indent else: if this_indent < first_indent: break print(' %3i %s' % (lineno, line[first_indent:].rstrip())) if not self.verbose: break def module_name(self, filename): #assert filename, startswith(self.basedir) mod = filename[len(self.basedir):].strip('/').strip(os.path.sep) mod = os.path.splitext(mod)[0] mod = mod.replace(os.path.sep, '.').replace('/', '.') return mod def relative_name(self, filename): #assert filename, startswith(self.basedir) name = filename[len(self.basedir):].strip('/').strip(os.path.sep) return name def display(self, filename, path): parts = '.'.join(path) if parts: parts = ':' + parts return self.module_name(filename) + parts pastescript-3.7.0/paste/script/help.py000066400000000000000000000036501474421447600200250ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php from .command import Command, get_commands from .command import parser as base_parser class HelpCommand(Command): summary = "Display help" usage = '[COMMAND]' max_args = 1 parser = Command.standard_parser() def command(self): if not self.args: self.generic_help() return name = self.args[0] commands = get_commands() if name not in commands: print('No such command: %s' % name) self.generic_help() return command = commands[name].load() runner = command(name) runner.run(['-h']) def generic_help(self): base_parser.print_help() print() commands_grouped = {} commands = get_commands() longest = max([len(n) for n in commands.keys()]) for name, command in commands.items(): try: command = command.load() except Exception as e: print('Cannot load command %s: %s' % (name, e)) continue if getattr(command, 'hidden', False): continue commands_grouped.setdefault( command.group_name, []).append((name, command)) commands_grouped = commands_grouped.items() commands_grouped = sorted(commands_grouped) print('Commands:') for group, commands in commands_grouped: if group: print(group + ':') commands.sort() for name, command in commands: print(' %s %s' % (self.pad(name, length=longest), command.summary)) #if command.description: # print self.indent_block(command.description, 4) print() pastescript-3.7.0/paste/script/interfaces.py000066400000000000000000000036721474421447600212240ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php class IAppInstall(object): """ The interface for objects in the entry point group ``paste.app_install`` """ def __init__(distribution, entry_group, entry_name): """ An object representing a specific application (the distribution is a pkg_resource.Distribution object), for the given entry point name in the given group. Right now the only group used for this is ``'paste.app_factory'``. """ def description(sys_config): """ Return a text description of the application and its configuration. ``sys_config`` is a dictionary representing the system configuration, and can be used for giving more explicit defaults if the application preparation uses the system configuration. It may be None, in which case the description should be more abstract. Applications are free to ignore ``sys_config``. """ def write_config(command, filename, sys_config): """ Write a fresh config file to ``filename``. ``command`` is a ``paste.script.command.Command`` object, and should be used for the actual operations. It handles things like simulation and verbosity. ``sys_config`` is (if given) a dictionary of system-wide configuration options. """ def setup_config(command, config_filename, config_section, sys_config): """ Set up the application, using ``command`` (to ensure simulate, etc). The application is described by the configuration file ``config_filename``. ``sys_config`` is the system configuration (though probably the values from it should have already been encorporated into the configuration file). """ pastescript-3.7.0/paste/script/paster-templates/000077500000000000000000000000001474421447600220115ustar00rootroot00000000000000pastescript-3.7.0/paste/script/paster-templates/basic_package/000077500000000000000000000000001474421447600245455ustar00rootroot00000000000000pastescript-3.7.0/paste/script/paster-templates/basic_package/+package+/000077500000000000000000000000001474421447600262665ustar00rootroot00000000000000pastescript-3.7.0/paste/script/paster-templates/basic_package/+package+/__init__.py000066400000000000000000000000021474421447600303670ustar00rootroot00000000000000# pastescript-3.7.0/paste/script/paster-templates/basic_package/docs/000077500000000000000000000000001474421447600254755ustar00rootroot00000000000000pastescript-3.7.0/paste/script/paster-templates/basic_package/docs/license.txt_tmpl000066400000000000000000001312551474421447600307230ustar00rootroot00000000000000{{if license_name == 'MIT'}} Copyright (c) 2007{{if author}} {{author}} and Contributors{{endif}} Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. {{elif license_name == 'GPL'}} GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. {{elif license_name == 'LGPL'}} GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! {{else}} {{skip_template()}} {{endif}} pastescript-3.7.0/paste/script/paster-templates/basic_package/setup.cfg000066400000000000000000000000631474421447600263650ustar00rootroot00000000000000[egg_info] tag_build = dev tag_svn_revision = true pastescript-3.7.0/paste/script/paster-templates/basic_package/setup.py_tmpl000066400000000000000000000015251474421447600273160ustar00rootroot00000000000000from setuptools import setup, find_packages import sys, os version = {{repr(version or "0.0")}} setup(name={{repr(project)}}, version=version, description="{{description or ''}}", long_description="""\ {{long_description or ''}}""", classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers keywords={{repr(keywords or '')}}, author={{repr(author or '')}}, author_email={{repr(author_email or '')}}, url={{repr(url or '')}}, license={{repr(license_name or '')}}, packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), include_package_data=True, zip_safe={{repr(bool(zip_safe or False))}}, install_requires=[ # -*- Extra requirements: -*- ], entry_points=""" # -*- Entry points: -*- """, ) pastescript-3.7.0/paste/script/pluginlib.py000066400000000000000000000102771474421447600210650ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php import os import pkg_resources def add_plugin(egg_info_dir, plugin_name): """ Add the plugin to the given distribution (or spec), in .egg-info/paster_plugins.txt """ fn = os.path.join(egg_info_dir, 'paster_plugins.txt') if not os.path.exists(fn): lines = [] else: f = open(fn) lines = [l.strip() for l in f.readlines() if l.strip()] f.close() if plugin_name in lines: # Nothing to do return lines.append(plugin_name) if not os.path.exists(os.path.dirname(fn)): os.makedirs(os.path.dirname(fn)) f = open(fn, 'w') for line in lines: f.write(line) f.write('\n') f.close() def remove_plugin(egg_info_dir, plugin_name): """ Remove the plugin to the given distribution (or spec), in .egg-info/paster_plugins.txt. Raises ValueError if the plugin is not in the file. """ fn = os.path.join(egg_info_dir, 'paster_plugins.txt') if not os.path.exists(fn): raise ValueError( "Cannot remove plugin from %s; file does not exist" % fn) f = open(fn) lines = [l.strip() for l in f.readlines() if l.strip()] f.close() for line in lines: # What about version specs? if line.lower() == plugin_name.lower(): break else: raise ValueError( "Plugin %s not found in file %s (from: %s)" % (plugin_name, fn, lines)) lines.remove(line) print('writing', lines) f = open(fn, 'w') for line in lines: f.write(line) f.write('\n') f.close() def find_egg_info_dir(dir): while 1: try: filenames = os.listdir(dir) except OSError: # Probably permission denied or something return None for fn in filenames: if (fn.endswith('.egg-info') and os.path.isdir(os.path.join(dir, fn))): return os.path.join(dir, fn) parent = os.path.dirname(dir) if parent == dir: # Top-most directory return None dir = parent def resolve_plugins(plugin_list): found = [] while plugin_list: plugin = plugin_list.pop() try: pkg_resources.require(plugin) except pkg_resources.DistributionNotFound as e: msg = '%sNot Found%s: %s (did you run python setup.py develop?)' if str(e) != plugin: e.args = (msg % (str(e) + ': ', ' for', plugin)), else: e.args = (msg % ('', '', plugin)), raise found.append(plugin) dist = get_distro(plugin) if dist.has_metadata('paster_plugins.txt'): data = dist.get_metadata('paster_plugins.txt') for add_plugin in parse_lines(data): if add_plugin not in found: plugin_list.append(add_plugin) return list(map(get_distro, found)) def get_distro(spec): return pkg_resources.get_distribution(spec) def load_commands_from_plugins(plugins): commands = {} for plugin in plugins: commands.update(pkg_resources.get_entry_map( plugin, group='paste.paster_command')) return commands def parse_lines(data): result = [] for line in data.splitlines(): line = line.strip() if line and not line.startswith('#'): result.append(line) return result def load_global_commands(): commands = {} for p in pkg_resources.iter_entry_points('paste.global_paster_command'): commands[p.name] = p return commands def egg_name(dist_name): return pkg_resources.to_filename(pkg_resources.safe_name(dist_name)) def egg_info_dir(base_dir, dist_name): all = [] for dir_extension in ['.'] + os.listdir(base_dir): full = os.path.join(base_dir, dir_extension, egg_name(dist_name)+'.egg-info') all.append(full) if os.path.exists(full): return full raise IOError("No egg-info directory found (looked in %s)" % ', '.join(all)) pastescript-3.7.0/paste/script/request.py000066400000000000000000000164121474421447600205650ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php import os import re import sys from urllib.parse import quote, urljoin from .command import Command, BadCommand from paste.deploy import loadapp from paste.wsgilib import raw_interactive class RequestCommand(Command): min_args = 2 usage = 'CONFIG_FILE URL [OPTIONS/ARGUMENTS]' takes_config_file = 1 summary = "Run a request for the described application" description = """\ This command makes an artifical request to a web application that uses a paste.deploy configuration file for the server and application. Use 'paster request config.ini /url' to request /url. Use 'paster post config.ini /url < data' to do a POST with the given request body. If the URL is relative (doesn't begin with /) it is interpreted as relative to /.command/. The variable environ['paste.command_request'] will be set to True in the request, so your application can distinguish these calls from normal requests. Note that you can pass options besides the options listed here; any unknown options will be passed to the application in environ['QUERY_STRING']. """ parser = Command.standard_parser(quiet=True) parser.add_option('-n', '--app-name', dest='app_name', metavar='NAME', help="Load the named application (default main)") parser.add_option('--config-var', dest='config_vars', metavar='NAME:VALUE', action='append', help="Variable to make available in the config for %()s substitution " "(you can use this option multiple times)") parser.add_option('--header', dest='headers', metavar='NAME:VALUE', action='append', help="Header to add to request (you can use this option multiple times)") parser.add_option('--display-headers', dest='display_headers', action='store_true', help='Display headers before the response body') ARG_OPTIONS = ['-n', '--app-name', '--config-var', '--header'] OTHER_OPTIONS = ['--display-headers'] ## FIXME: some kind of verbosity? ## FIXME: allow other methods than POST and GET? _scheme_re = re.compile(r'^[a-z][a-z]+:', re.I) def command(self): vars = {} app_spec = self.args[0] url = self.args[1] url = urljoin('/.command/', url) if self.options.config_vars: for item in self.option.config_vars: if ':' not in item: raise BadCommand( "Bad option, should be name:value : --config-var=%s" % item) name, value = item.split(':', 1) vars[name] = value headers = {} if self.options.headers: for item in self.options.headers: if ':' not in item: raise BadCommand( "Bad option, should be name:value : --header=%s" % item) name, value = item.split(':', 1) headers[name] = value.strip() if not self._scheme_re.search(app_spec): app_spec = 'config:'+app_spec if self.options.app_name: if '#' in app_spec: app_spec = app_spec.split('#', 1)[0] app_spec = app_spec + '#' + self.options.app_name app = loadapp(app_spec, relative_to=os.getcwd(), global_conf=vars) if self.command_name.lower() == 'post': request_method = 'POST' else: request_method = 'GET' qs = [] for item in self.args[2:]: if '=' in item: item = quote(item.split('=', 1)[0]) + '=' + quote(item.split('=', 1)[1]) else: item = quote(item) qs.append(item) qs = '&'.join(qs) environ = { 'REQUEST_METHOD': request_method, ## FIXME: shouldn't be static (an option?): 'CONTENT_TYPE': 'text/plain', 'wsgi.run_once': True, 'wsgi.multithread': False, 'wsgi.multiprocess': False, 'wsgi.errors': sys.stderr, 'QUERY_STRING': qs, 'HTTP_ACCEPT': 'text/plain;q=1.0, */*;q=0.1', 'paste.command_request': True, } if request_method == 'POST': environ['wsgi.input'] = sys.stdin environ['CONTENT_LENGTH'] = '-1' for name, value in headers.items(): if name.lower() == 'content-type': name = 'CONTENT_TYPE' else: name = 'HTTP_'+name.upper().replace('-', '_') environ[name] = value status, headers, output, errors = raw_interactive(app, url, **environ) assert not errors, "errors should be printed directly to sys.stderr" if self.options.display_headers: for name, value in headers: sys.stdout.write('%s: %s\n' % (name, value)) sys.stdout.write('\n') sys.stdout.flush() sys.stdout.buffer.write(output) sys.stdout.buffer.flush() sys.stdout.flush() status_int = int(status.split()[0]) if status_int != 200: return status_int def parse_args(self, args): if args == ['-h']: Command.parse_args(self, args) return # These are the arguments parsed normally: normal_args = [] # And these are arguments passed to the URL: extra_args = [] # This keeps track of whether we have the two required positional arguments: pos_args = 0 while args: start = args[0] if not start.startswith('-'): if pos_args < 2: pos_args += 1 normal_args.append(start) args.pop(0) continue else: normal_args.append(start) args.pop(0) continue else: found = False for option in self.ARG_OPTIONS: if start == option: normal_args.append(start) args.pop(0) if not args: raise BadCommand( "Option %s takes an argument" % option) normal_args.append(args.pop(0)) found = True break elif start.startswith(option+'='): normal_args.append(start) args.pop(0) found = True break if found: continue if start in self.OTHER_OPTIONS: normal_args.append(start) args.pop(0) continue extra_args.append(start) args.pop(0) Command.parse_args(self, normal_args) # Add the extra arguments back in: self.args = self.args + extra_args pastescript-3.7.0/paste/script/serve.py000066400000000000000000000551241474421447600202240ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php # @@: This should be moved to paste.deploy # For discussion of daemonizing: # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731 # Code taken also from QP: # http://www.mems-exchange.org/software/qp/ # From lib/site.py import atexit import errno import logging import os import re import subprocess import sys import threading import time import traceback from paste.deploy import loadapp, loadserver from paste.script.command import Command, BadCommand MAXFD = 1024 jython = sys.platform.startswith('java') class DaemonizeException(Exception): pass class ServeCommand(Command): min_args = 0 usage = 'CONFIG_FILE [start|stop|restart|status] [var=value]' takes_config_file = 1 summary = "Serve the described application" description = """\ This command serves a web application that uses a paste.deploy configuration file for the server and application. If start/stop/restart is given, then --daemon is implied, and it will start (normal operation), stop (--stop-daemon), or do both. You can also include variable assignments like 'http_port=8080' and then use %(http_port)s in your config files. """ # used by subclasses that configure apps and servers differently requires_config_file = True parser = Command.standard_parser(quiet=True) parser.add_option('-n', '--app-name', dest='app_name', metavar='NAME', help="Load the named application (default main)") parser.add_option('-s', '--server', dest='server', metavar='SERVER_TYPE', help="Use the named server.") parser.add_option('--server-name', dest='server_name', metavar='SECTION_NAME', help="Use the named server as defined in the configuration file (default: main)") if hasattr(os, 'fork'): parser.add_option('--daemon', dest="daemon", action="store_true", help="Run in daemon (background) mode") parser.add_option('--pid-file', dest='pid_file', metavar='FILENAME', help="Save PID to file (default to paster.pid if running in daemon mode)") parser.add_option('--log-file', dest='log_file', metavar='LOG_FILE', help="Save output to the given log file (redirects stdout)") parser.add_option('--reload', dest='reload', action='store_true', help="Use auto-restart file monitor") parser.add_option('--reload-interval', dest='reload_interval', default=1, help="Seconds between checking files (low number can cause significant CPU usage)") parser.add_option('--monitor-restart', dest='monitor_restart', action='store_true', help="Auto-restart server if it dies") parser.add_option('--status', action='store_true', dest='show_status', help="Show the status of the (presumably daemonized) server") if hasattr(os, 'setuid'): # I don't think these are available on Windows parser.add_option('--user', dest='set_user', metavar="USERNAME", help="Set the user (usually only possible when run as root)") parser.add_option('--group', dest='set_group', metavar="GROUP", help="Set the group (usually only possible when run as root)") parser.add_option('--stop-daemon', dest='stop_daemon', action='store_true', help='Stop a daemonized server (given a PID file, or default paster.pid file)') if jython: parser.add_option('--disable-jython-reloader', action='store_true', dest='disable_jython_reloader', help="Disable the Jython reloader") _scheme_re = re.compile(r'^[a-z][a-z]+:', re.I) default_verbosity = 1 _reloader_environ_key = 'PYTHON_RELOADER_SHOULD_RUN' _monitor_environ_key = 'PASTE_MONITOR_SHOULD_RUN' possible_subcommands = ('start', 'stop', 'restart', 'status') def command(self): if self.options.stop_daemon: return self.stop_daemon() if not hasattr(self.options, 'set_user'): # Windows case: self.options.set_user = self.options.set_group = None # @@: Is this the right stage to set the user at? self.change_user_group( self.options.set_user, self.options.set_group) if self.requires_config_file: if not self.args: raise BadCommand('You must give a config file') app_spec = self.args[0] if (len(self.args) > 1 and self.args[1] in self.possible_subcommands): cmd = self.args[1] restvars = self.args[2:] else: cmd = None restvars = self.args[1:] else: app_spec = "" if (self.args and self.args[0] in self.possible_subcommands): cmd = self.args[0] restvars = self.args[1:] else: cmd = None restvars = self.args[:] if (getattr(self.options, 'daemon', False) and getattr(self.options, 'reload', False)): raise BadCommand('The --daemon and --reload options may not be used together') jython_monitor = False if self.options.reload: if jython and not self.options.disable_jython_reloader: # JythonMonitor raises the special SystemRestart # exception that'll cause the Jython interpreter to # reload in the existing Java process (avoiding # subprocess startup time) try: from paste.reloader import JythonMonitor except ImportError: pass else: jython_monitor = JythonMonitor(poll_interval=int( self.options.reload_interval)) if self.requires_config_file: jython_monitor.watch_file(self.args[0]) if not jython_monitor: if os.environ.get(self._reloader_environ_key): from paste import reloader if self.verbose > 1: print('Running reloading file monitor') reloader.install(int(self.options.reload_interval)) if self.requires_config_file: reloader.watch_file(self.args[0]) else: return self.restart_with_reloader() if cmd not in (None, 'start', 'stop', 'restart', 'status'): raise BadCommand( 'Error: must give start|stop|restart (not %s)' % cmd) if cmd == 'status' or self.options.show_status: return self.show_status() if cmd == 'restart' or cmd == 'stop': result = self.stop_daemon() if result: if cmd == 'restart': print("Could not stop daemon; aborting") else: print("Could not stop daemon") return result if cmd == 'stop': return result self.options.daemon = True if cmd == 'start': self.options.daemon = True app_name = self.options.app_name vars = self.parse_vars(restvars) if not self._scheme_re.search(app_spec): app_spec = 'config:' + app_spec server_name = self.options.server_name if self.options.server: server_spec = 'egg:PasteScript' assert server_name is None server_name = self.options.server else: server_spec = app_spec base = os.getcwd() if getattr(self.options, 'daemon', False): if not self.options.pid_file: self.options.pid_file = 'paster.pid' if not self.options.log_file: self.options.log_file = 'paster.log' # Ensure the log file is writeable if self.options.log_file: try: writeable_log_file = open(self.options.log_file, 'a') except IOError as ioe: msg = 'Error: Unable to write to log file: %s' % ioe raise BadCommand(msg) writeable_log_file.close() # Ensure the pid file is writeable if self.options.pid_file: try: writeable_pid_file = open(self.options.pid_file, 'a') except IOError as ioe: msg = 'Error: Unable to write to pid file: %s' % ioe raise BadCommand(msg) writeable_pid_file.close() if getattr(self.options, 'daemon', False): try: self.daemonize() except DaemonizeException as ex: if self.verbose > 0: print(str(ex)) return if (self.options.monitor_restart and not os.environ.get(self._monitor_environ_key)): return self.restart_with_monitor() if self.options.pid_file: self.record_pid(self.options.pid_file) if self.options.log_file: stdout_log = LazyWriter(self.options.log_file, 'a') sys.stdout = stdout_log sys.stderr = stdout_log logging.basicConfig(stream=stdout_log) log_fn = app_spec if log_fn.startswith('config:'): log_fn = app_spec[len('config:'):] elif log_fn.startswith('egg:'): log_fn = None if log_fn: log_fn = os.path.join(base, log_fn) self.logging_file_config(log_fn) try: server = self.loadserver(server_spec, name=server_name, relative_to=base, global_conf=vars) app = self.loadapp(app_spec, name=app_name, relative_to=base, global_conf=vars) except SyntaxError as e: if self.options.reload and os.environ.get(self._reloader_environ_key): traceback.print_exc() reloader.watch_file(e.filename) while True: time.sleep(60*60) else: raise if self.verbose > 0: if hasattr(os, 'getpid'): msg = 'Starting server in PID %i.' % os.getpid() else: msg = 'Starting server.' print(msg) def serve(): try: server(app) except (SystemExit, KeyboardInterrupt) as e: if self.verbose > 1: raise if str(e): msg = ' '+str(e) else: msg = '' print('Exiting%s (-v to see traceback)' % msg) if jython_monitor: # JythonMonitor has to be ran from the main thread threading.Thread(target=serve).start() print('Starting Jython file monitor') jython_monitor.periodic_reload() else: serve() def loadserver(self, server_spec, name, relative_to, **kw): return loadserver( server_spec, name=name, relative_to=relative_to, **kw) def loadapp(self, app_spec, name, relative_to, **kw): return loadapp( app_spec, name=name, relative_to=relative_to, **kw) def daemonize(self): pid = live_pidfile(self.options.pid_file) if pid: raise DaemonizeException( "Daemon is already running (PID: %s from PID file %s)" % (pid, self.options.pid_file)) if self.verbose > 0: print('Entering daemon mode') pid = os.fork() if pid: # The forked process also has a handle on resources, so we # *don't* want proper termination of the process, we just # want to exit quick (which os._exit() does) os._exit(0) # Make this the session leader os.setsid() # Fork again for good measure! pid = os.fork() if pid: os._exit(0) # @@: Should we set the umask and cwd now? import resource # Resource usage information. maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] if (maxfd == resource.RLIM_INFINITY): maxfd = MAXFD # Iterate through and close all file descriptors. for fd in range(0, maxfd): try: os.close(fd) except OSError: # ERROR, fd wasn't open to begin with (ignored) pass if (hasattr(os, "devnull")): REDIRECT_TO = os.devnull else: REDIRECT_TO = "/dev/null" os.open(REDIRECT_TO, os.O_RDWR) # standard input (0) # Duplicate standard input to standard output and standard error. os.dup2(0, 1) # standard output (1) os.dup2(0, 2) # standard error (2) def record_pid(self, pid_file): pid = os.getpid() if self.verbose > 1: print('Writing PID %s to %s' % (pid, pid_file)) f = open(pid_file, 'w') f.write(str(pid)) f.close() atexit.register(_remove_pid_file, pid, pid_file, self.verbose) def stop_daemon(self): pid_file = self.options.pid_file or 'paster.pid' if not os.path.exists(pid_file): print('No PID file exists in %s' % pid_file) return 1 pid = read_pidfile(pid_file) if not pid: print("Not a valid PID file in %s" % pid_file) return 1 pid = live_pidfile(pid_file) if not pid: print("PID in %s is not valid (deleting)" % pid_file) try: os.unlink(pid_file) except (OSError, IOError) as e: print("Could not delete: %s" % e) return 2 return 1 for j in range(10): if not live_pidfile(pid_file): break import signal os.kill(pid, signal.SIGINT) time.sleep(1) for j in range(10): if not live_pidfile(pid_file): break import signal os.kill(pid, signal.SIGTERM) time.sleep(1) else: print("failed to kill web process %s" % pid) return 3 if os.path.exists(pid_file): os.unlink(pid_file) return 0 def show_status(self): pid_file = self.options.pid_file or 'paster.pid' if not os.path.exists(pid_file): print('No PID file %s' % pid_file) return 1 pid = read_pidfile(pid_file) if not pid: print('No PID in file %s' % pid_file) return 1 pid = live_pidfile(pid_file) if not pid: print('PID %s in %s is not running' % (pid, pid_file)) return 1 print('Server running in PID %s' % pid) return 0 def restart_with_reloader(self): self.restart_with_monitor(reloader=True) def restart_with_monitor(self, reloader=False): if self.verbose > 0: if reloader: print('Starting subprocess with file monitor') else: print('Starting subprocess with monitor parent') while 1: args = [self.quote_first_command_arg(sys.executable)] + sys.argv new_environ = os.environ.copy() if reloader: new_environ[self._reloader_environ_key] = 'true' else: new_environ[self._monitor_environ_key] = 'true' proc = None try: try: _turn_sigterm_into_systemexit() proc = subprocess.Popen(args, env=new_environ) exit_code = proc.wait() proc = None except KeyboardInterrupt: print('^C caught in monitor process') if self.verbose > 1: raise return 1 finally: if (proc is not None and hasattr(os, 'kill')): import signal try: os.kill(proc.pid, signal.SIGTERM) except (OSError, IOError): pass if reloader: # Reloader always exits with code 3; but if we are # a monitor, any exit code will restart if exit_code != 3: return exit_code if self.verbose > 0: print('-'*20, 'Restarting', '-'*20) def change_user_group(self, user, group): if not user and not group: return import pwd, grp uid = gid = None if group: try: gid = int(group) group = grp.getgrgid(gid).gr_name except ValueError: import grp try: entry = grp.getgrnam(group) except KeyError: raise BadCommand( "Bad group: %r; no such group exists" % group) gid = entry.gr_gid try: uid = int(user) user = pwd.getpwuid(uid).pw_name except ValueError: try: entry = pwd.getpwnam(user) except KeyError: raise BadCommand( "Bad username: %r; no such user exists" % user) if not gid: gid = entry.pw_gid uid = entry.pw_uid if self.verbose > 0: print('Changing user to %s:%s (%s:%s)' % ( user, group or '(unknown)', uid, gid)) if hasattr(os, 'initgroups'): os.initgroups(user, gid) else: os.setgroups([e.gr_gid for e in grp.getgrall() if user in e.gr_mem] + [gid]) if gid: os.setgid(gid) if uid: os.setuid(uid) class LazyWriter(object): """ File-like object that opens a file lazily when it is first written to. """ def __init__(self, filename, mode='w'): self.filename = filename self.fileobj = None self.lock = threading.Lock() self.mode = mode def open(self): if self.fileobj is None: self.lock.acquire() try: if self.fileobj is None: self.fileobj = open(self.filename, self.mode) finally: self.lock.release() return self.fileobj def write(self, text): fileobj = self.open() fileobj.write(text) fileobj.flush() def writelines(self, text): fileobj = self.open() fileobj.writelines(text) fileobj.flush() def flush(self): self.open().flush() def live_pidfile(pidfile): """(pidfile:str) -> int | None Returns an int found in the named file, if there is one, and if there is a running process with that process id. Return None if no such process exists. """ pid = read_pidfile(pidfile) if pid: try: os.kill(int(pid), 0) return pid except OSError as e: if e.errno == errno.EPERM: return pid return None def read_pidfile(filename): if os.path.exists(filename): try: f = open(filename) content = f.read() f.close() return int(content.strip()) except (ValueError, IOError): return None else: return None def _remove_pid_file(written_pid, filename, verbosity): current_pid = os.getpid() if written_pid != current_pid: # A forked process must be exiting, not the process that # wrote the PID file return if not os.path.exists(filename): return f = open(filename) content = f.read().strip() f.close() try: pid_in_file = int(content) except ValueError: pass else: if pid_in_file != current_pid: print("PID file %s contains %s, not expected PID %s" % ( filename, pid_in_file, current_pid)) return if verbosity > 0: print("Removing PID file %s" % filename) try: os.unlink(filename) return except OSError as e: # Record, but don't give traceback print("Cannot remove PID file: %s" % e) # well, at least lets not leave the invalid PID around... try: f = open(filename, 'w') f.write('') f.close() except OSError as e: print('Stale PID left in file: %s (%e)' % (filename, e)) else: print('Stale PID removed') def ensure_port_cleanup(bound_addresses, maxtries=30, sleeptime=2): """ This makes sure any open ports are closed. Does this by connecting to them until they give connection refused. Servers should call like:: import paste.script ensure_port_cleanup([80, 443]) """ atexit.register(_cleanup_ports, bound_addresses, maxtries=maxtries, sleeptime=sleeptime) def _cleanup_ports(bound_addresses, maxtries=30, sleeptime=2): # Wait for the server to bind to the port. import socket import errno for bound_address in bound_addresses: for attempt in range(maxtries): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: sock.connect(bound_address) except socket.error as e: if e.args[0] != errno.ECONNREFUSED: raise break else: time.sleep(sleeptime) else: raise SystemExit('Timeout waiting for port.') sock.close() def _turn_sigterm_into_systemexit(): """ Attempts to turn a SIGTERM exception into a SystemExit exception. """ try: import signal except ImportError: return def handle_term(signo, frame): raise SystemExit signal.signal(signal.SIGTERM, handle_term) pastescript-3.7.0/paste/script/templates.py000066400000000000000000000234421474421447600210740ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php import sys import os import inspect from . import copydir from . import command from paste.util.template import paste_script_template_renderer class Template: # Subclasses must define: # _template_dir (or template_dir()) # summary # Variables this template uses (mostly for documentation now) # a list of instances of var() vars = [] # Eggs that should be added as plugins: egg_plugins = [] # Templates that must be applied first: required_templates = [] # Use Cheetah for substituting templates: use_cheetah = False # If true, then read all the templates to find the variables: read_vars_from_templates = False # You can also give this function/method to use something other # than Cheetah or string.Template. The function should be of the # signature template_renderer(content, vars, filename=filename). # Careful you don't turn this into a method by putting a function # here (without staticmethod)! template_renderer = None def __init__(self, name): self.name = name self._read_vars = None def module_dir(self): """Returns the module directory of this template.""" mod = sys.modules[self.__class__.__module__] return os.path.dirname(mod.__file__) def template_dir(self): assert self._template_dir is not None, ( "Template %r didn't set _template_dir" % self) if isinstance( self._template_dir, tuple): return self._template_dir else: return os.path.join(self.module_dir(), self._template_dir) def run(self, command, output_dir, vars): self.pre(command, output_dir, vars) self.write_files(command, output_dir, vars) self.post(command, output_dir, vars) def check_vars(self, vars, cmd): expect_vars = self.read_vars(cmd) if not expect_vars: # Assume that variables aren't defined return vars converted_vars = {} unused_vars = vars.copy() errors = [] for var in expect_vars: if var.name not in unused_vars: if cmd.interactive: prompt = 'Enter %s' % var.full_description() response = cmd.challenge(prompt, var.default, var.should_echo) converted_vars[var.name] = response elif var.default is command.NoDefault: errors.append('Required variable missing: %s' % var.full_description()) else: converted_vars[var.name] = var.default else: converted_vars[var.name] = unused_vars.pop(var.name) if errors: raise command.BadCommand( 'Errors in variables:\n%s' % '\n'.join(errors)) converted_vars.update(unused_vars) vars.update(converted_vars) return converted_vars def read_vars(self, command=None): if self._read_vars is not None: return self._read_vars assert (not self.read_vars_from_templates or self.use_cheetah), ( "You can only read variables from templates if using Cheetah") if not self.read_vars_from_templates: self._read_vars = self.vars return self.vars vars = self.vars[:] var_names = [var.name for var in self.vars] read_vars = find_args_in_dir( self.template_dir(), verbose=command and command.verbose > 1).items() read_vars.sort() for var_name, var in read_vars: if var_name not in var_names: vars.append(var) self._read_vars = vars return vars def write_files(self, command, output_dir, vars): template_dir = self.template_dir() if not os.path.exists(output_dir): print("Creating directory %s" % output_dir) if not command.simulate: # Don't let copydir create this top-level directory, # since copydir will svn add it sometimes: os.makedirs(output_dir) copydir.copy_dir(template_dir, output_dir, vars, verbosity=command.verbose, simulate=command.options.simulate, interactive=command.interactive, overwrite=command.options.overwrite, indent=1, use_cheetah=self.use_cheetah, template_renderer=self.template_renderer) def print_vars(self, indent=0): vars = self.read_vars() var.print_vars(vars) def pre(self, command, output_dir, vars): """ Called before template is applied. """ pass def post(self, command, output_dir, vars): """ Called after template is applied. """ pass NoDefault = command.NoDefault class var(object): def __init__(self, name, description, default='', should_echo=True): self.name = name self.description = description self.default = default self.should_echo = should_echo def __repr__(self): return '<%s %s default=%r should_echo=%s>' % ( self.__class__.__name__, self.name, self.default, self.should_echo) def full_description(self): if self.description: return '%s (%s)' % (self.name, self.description) else: return self.name def print_vars(cls, vars, indent=0): max_name = max([len(v.name) for v in vars]) for var in vars: if var.description: print('%s%s%s %s' % ( ' '*indent, var.name, ' '*(max_name-len(var.name)), var.description)) else: print(' %s' % var.name) if var.default is not command.NoDefault: print(' default: %r' % var.default) if var.should_echo is True: print(' should_echo: %s' % var.should_echo) print() print_vars = classmethod(print_vars) class BasicPackage(Template): _template_dir = 'paster-templates/basic_package' summary = "A basic setuptools-enabled package" vars = [ var('version', 'Version (like 0.1)'), var('description', 'One-line description of the package'), var('long_description', 'Multi-line description (in reST)'), var('keywords', 'Space-separated keywords/tags'), var('author', 'Author name'), var('author_email', 'Author email'), var('url', 'URL of homepage'), var('license_name', 'License name'), var('zip_safe', 'True/False: if the package can be distributed as a .zip file', default=False), ] template_renderer = staticmethod(paste_script_template_renderer) _skip_variables = ['VFN', 'currentTime', 'self', 'VFFSL', 'dummyTrans', 'getmtime', 'trans'] def find_args_in_template(template): if isinstance(template, str): # Treat as filename: import Cheetah.Template template = Cheetah.Template.Template(file=template) if not hasattr(template, 'body'): # Don't know... return None method = template.body args, varargs, varkw, defaults = inspect.getargspec(method) defaults=list(defaults or []) vars = [] while args: if len(args) == len(defaults): default = defaults.pop(0) else: default = command.NoDefault arg = args.pop(0) if arg in _skip_variables: continue # @@: No way to get description yet vars.append( var(arg, description=None, default=default)) return vars def find_args_in_dir(dir, verbose=False): all_vars = {} for fn in os.listdir(dir): if fn.startswith('.') or fn == 'CVS' or fn == '_darcs': continue full = os.path.join(dir, fn) if os.path.isdir(full): inner_vars = find_args_in_dir(full) elif full.endswith('_tmpl'): inner_vars = {} found = find_args_in_template(full) if found is None: # Couldn't read variables if verbose: print('Template %s has no parseable variables' % full) continue for var in found: inner_vars[var.name] = var else: # Not a template, don't read it continue if verbose: print('Found variable(s) %s in Template %s' % ( ', '.join(inner_vars.keys()), full)) for var_name, var in inner_vars.items(): # Easy case: if var_name not in all_vars: all_vars[var_name] = var continue # Emit warnings if the variables don't match well: cur_var = all_vars[var_name] if not cur_var.description: cur_var.description = var.description elif (cur_var.description and var.description and var.description != cur_var.description): print(( "Variable descriptions do not match: %s: %s and %s" % (var_name, cur_var.description, var.description)), file=sys.stderr) if (cur_var.default is not command.NoDefault and var.default is not command.NoDefault and cur_var.default != var.default): print(( "Variable defaults do not match: %s: %r and %r" % (var_name, cur_var.default, var.default)), file=sys.stderr) return all_vars pastescript-3.7.0/paste/script/testapp.py000066400000000000000000000054551474421447600205620ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php try: import html except ImportError: import cgi as html html_page_template = ''' Test Application

Test Application: Working!

%(environ)s

Note: to see an error report, append ?error=true to the URL

''' html_row_template = ''' %(key)s %(value_literal)s ''' text_page_template = '%(environ)s' text_row_template = '%(key)s: %(value_repr)s\n' def make_literal(value): value = html.escape(value, 1) value = value.replace('\n\r', '\n') value = value.replace('\r', '\n') value = value.replace('\n', '
\n') return value class TestApplication(object): """ A test WSGI application, that prints out all the environmental variables, and if you add ``?error=t`` to the URL it will deliberately throw an exception. """ def __init__(self, global_conf=None, text=False): self.global_conf = global_conf self.text = text def __call__(self, environ, start_response): if environ.get('QUERY_STRING', '').find('error=') >= 0: assert 0, "Here is your error report, ordered and delivered" if self.text: page_template = text_page_template row_template = text_row_template content_type = 'text/plain; charset=utf8' else: page_template = html_page_template row_template = html_row_template content_type = 'text/html; charset=utf8' keys = sorted(environ.keys()) rows = [] for key in keys: data = {'key': key} value = environ[key] data['value'] = value try: value = repr(value) except Exception as e: value = 'Cannot use repr(): %s' % e data['value_repr'] = value data['value_literal'] = make_literal(value) row = row_template % data rows.append(row) rows = ''.join(rows) page = page_template % {'environ': rows} if isinstance(page, str): page = page.encode('utf8') headers = [('Content-type', content_type)] start_response('200 OK', headers) return [page] def make_test_application(global_conf, text=False, lint=False): from paste.deploy.converters import asbool text = asbool(text) lint = asbool(lint) app = TestApplication(global_conf=global_conf, text=text) if lint: from paste.lint import middleware app = middleware(app) return app make_test_application.__doc__ = TestApplication.__doc__ pastescript-3.7.0/paste/script/twisted_web2_server.py000066400000000000000000000016111474421447600230600ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php # @@: THIS IS INCOMPLETE! def run_twisted(wsgi_app, global_conf, host='127.0.0.1', port='8080'): host = host or None import twisted.web2.wsgi import twisted.web2.log import twisted.web2.channel import twisted.web2.server import twisted.internet.reactor wsgi_resource = twisted.web2.wsgi.WSGIResource(wsgi_app) resource = twisted.web2.log.LogWrapperResource(wsgi_resource) twisted.web2.log.DefaultCommonAccessLoggingObserver().start() site = twisted.web2.server.Site(resource) factory = twisted.web2.channel.HTTPFactory(site) # --- start reactor for listen port twisted.internet.reactor.listenTCP(int(port), factory, interface=host) twisted.internet.reactor.run() pastescript-3.7.0/paste/script/util/000077500000000000000000000000001474421447600174745ustar00rootroot00000000000000pastescript-3.7.0/paste/script/util/__init__.py000066400000000000000000000002531474421447600216050ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php # pastescript-3.7.0/paste/script/util/logging_config.py000066400000000000000000000276371474421447600230400ustar00rootroot00000000000000# Copyright 2001-2005 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, # provided that the above copyright notice appear in all copies and that # both that copyright notice and this permission notice appear in # supporting documentation, and that the name of Vinay Sajip # not be used in advertising or publicity pertaining to distribution # of the software without specific, written prior permission. # VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL # VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER # IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """ Configuration functions for the logging package for Python. The core package is based on PEP 282 and comments thereto in comp.lang.python, and influenced by Apache's log4j system. Should work under Python versions >= 1.5.2, except that source line information is not available unless 'sys._getframe()' is. Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved. To use, simply 'import logging' and log away! """ import logging import logging.handlers import os import socket import struct import sys import traceback try: import threading except ImportError: thread = None from socketserver import ThreadingTCPServer, StreamRequestHandler DEFAULT_LOGGING_CONFIG_PORT = 9030 if sys.platform == "win32": RESET_ERROR = 10054 #WSAECONNRESET else: RESET_ERROR = 104 #ECONNRESET # # The following code implements a socket listener for on-the-fly # reconfiguration of logging. # # _listener holds the server object doing the listening _listener = None log_levelNames = { 'CRITICAL' : logging.CRITICAL, 'ERROR' : logging.ERROR, 'WARN' : logging.WARNING, 'WARNING' : logging.WARNING, 'INFO' : logging.INFO, 'DEBUG' : logging.DEBUG, 'NOTSET' : logging.NOTSET, } def fileConfig(fname, defaults=None): """ Read the logging configuration from a ConfigParser-format file. This can be called several times from an application, allowing an end user the ability to select from various pre-canned configurations (if the developer provides a mechanism to present the choices and load the chosen configuration). In versions of ConfigParser which have the readfp method [typically shipped in 2.x versions of Python], you can pass in a file-like object rather than a filename, in which case the file-like object will be read using readfp. """ import configparser cp = configparser.RawConfigParser(defaults) if hasattr(cp, 'readfp') and hasattr(fname, 'readline'): cp.readfp(fname) else: cp.read(fname) formatters = _create_formatters(cp) # critical section with logging._lock: logging._handlers.clear() if hasattr(logging, '_handlerList'): del logging._handlerList[:] # Handlers add themselves to logging._handlers handlers = _install_handlers(cp, formatters) _install_loggers(cp, handlers) def _resolve(name): """Resolve a dotted name to a global object.""" name = name.split('.') used = name.pop(0) found = __import__(used) for n in name: used = used + '.' + n try: found = getattr(found, n) except AttributeError: __import__(used) found = getattr(found, n) return found def _create_formatters(cp): """Create and return formatters""" flist = cp.get("formatters", "keys") if not len(flist): return {} flist = flist.split(",") formatters = {} for form in flist: form = form.strip() sectname = "formatter_%s" % form opts = cp.options(sectname) if "format" in opts: fs = cp.get(sectname, "format") else: fs = None if "datefmt" in opts: dfs = cp.get(sectname, "datefmt") else: dfs = None c = logging.Formatter if "class" in opts: class_name = cp.get(sectname, "class") if class_name: c = _resolve(class_name) f = c(fs, dfs) formatters[form] = f return formatters def _install_handlers(cp, formatters): """Install and return handlers""" hlist = cp.get("handlers", "keys") if not len(hlist): return {} hlist = hlist.split(",") handlers = {} fixups = [] #for inter-handler references for hand in hlist: hand = hand.strip() sectname = "handler_%s" % hand klass = cp.get(sectname, "class") opts = cp.options(sectname) if "formatter" in opts: fmt = cp.get(sectname, "formatter") else: fmt = "" try: klass = eval(klass, vars(logging)) except (AttributeError, NameError): klass = _resolve(klass) args = cp.get(sectname, "args") args = eval(args, vars(logging)) h = klass(*args) if "level" in opts: level = cp.get(sectname, "level") h.setLevel(log_levelNames[level]) if len(fmt): h.setFormatter(formatters[fmt]) #temporary hack for FileHandler and MemoryHandler. if klass == logging.handlers.MemoryHandler: if "target" in opts: target = cp.get(sectname,"target") else: target = "" if len(target): #the target handler may not be loaded yet, so keep for later... fixups.append((h, target)) handlers[hand] = h #now all handlers are loaded, fixup inter-handler references... for h, t in fixups: h.setTarget(handlers[t]) return handlers def _install_loggers(cp, handlers): """Create and install loggers""" # configure the root first llist = cp.get("loggers", "keys") llist = llist.split(",") llist = [x.strip() for x in llist] llist.remove("root") sectname = "logger_root" root = logging.root log = root opts = cp.options(sectname) if "level" in opts: level = cp.get(sectname, "level") log.setLevel(log_levelNames[level]) for h in root.handlers[:]: root.removeHandler(h) hlist = cp.get(sectname, "handlers") if len(hlist): hlist = hlist.split(",") for hand in hlist: log.addHandler(handlers[hand.strip()]) #and now the others... #we don't want to lose the existing loggers, #since other threads may have pointers to them. #existing is set to contain all existing loggers, #and as we go through the new configuration we #remove any which are configured. At the end, #what's left in existing is the set of loggers #which were in the previous configuration but #which are not in the new configuration. existing = list(root.manager.loggerDict.keys()) #now set up the new ones... for log in llist: sectname = "logger_%s" % log qn = cp.get(sectname, "qualname") opts = cp.options(sectname) if "propagate" in opts: propagate = cp.getint(sectname, "propagate") else: propagate = 1 logger = logging.getLogger(qn) if qn in existing: existing.remove(qn) if "level" in opts: level = cp.get(sectname, "level") logger.setLevel(log_levelNames[level]) for h in logger.handlers[:]: logger.removeHandler(h) logger.propagate = propagate logger.disabled = 0 hlist = cp.get(sectname, "handlers") if len(hlist): hlist = hlist.split(",") for hand in hlist: logger.addHandler(handlers[hand.strip()]) #Disable any old loggers. There's no point deleting #them as other threads may continue to hold references #and by disabling them, you stop them doing any logging. for log in existing: root.manager.loggerDict[log].disabled = 1 def listen(port=DEFAULT_LOGGING_CONFIG_PORT): """ Start up a socket server on the specified port, and listen for new configurations. These will be sent as a file suitable for processing by fileConfig(). Returns a Thread object on which you can call start() to start the server, and which you can join() when appropriate. To stop the server, call stopListening(). """ if not thread: raise NotImplementedError("listen() needs threading to work") class ConfigStreamHandler(StreamRequestHandler): """ Handler for a logging configuration request. It expects a completely new logging configuration and uses fileConfig to install it. """ def handle(self): """ Handle a request. Each request is expected to be a 4-byte length, packed using struct.pack(">L", n), followed by the config file. Uses fileConfig() to do the grunt work. """ import tempfile try: conn = self.connection chunk = conn.recv(4) if len(chunk) == 4: slen = struct.unpack(">L", chunk)[0] chunk = self.connection.recv(slen) while len(chunk) < slen: chunk = chunk + conn.recv(slen - len(chunk)) #Apply new configuration. We'd like to be able to #create a StringIO and pass that in, but unfortunately #1.5.2 ConfigParser does not support reading file #objects, only actual files. So we create a temporary #file and remove it later. file = tempfile.mktemp(".ini") f = open(file, "w") f.write(chunk) f.close() try: fileConfig(file) except (KeyboardInterrupt, SystemExit): raise except: traceback.print_exc() os.remove(file) except socket.error as e: if type(e.args) != tuple: raise else: errcode = e.args[0] if errcode != RESET_ERROR: raise class ConfigSocketReceiver(ThreadingTCPServer): """ A simple TCP socket-based logging config receiver. """ allow_reuse_address = 1 def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT, handler=None): ThreadingTCPServer.__init__(self, (host, port), handler) with logging._lock: self.abort = 0 self.timeout = 1 def serve_until_stopped(self): import select abort = 0 while not abort: rd, wr, ex = select.select([self.socket.fileno()], [], [], self.timeout) if rd: self.handle_request() with logging._lock: abort = self.abort def serve(rcvr, hdlr, port): server = rcvr(port=port, handler=hdlr) global _listener with logging._lock: _listener = server server.serve_until_stopped() return threading.Thread(target=serve, args=(ConfigSocketReceiver, ConfigStreamHandler, port)) def stopListening(): """ Stop the listening server which was created with a call to listen(). """ global _listener if _listener: with logging._lock: _listener.abort = 1 _listener = None pastescript-3.7.0/paste/script/util/secret.py000066400000000000000000000020251474421447600213320ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php """ Create random secrets. """ import base64 import os import random def random_bytes(length): """ Return a string of the given length. Uses ``os.urandom`` if it can, or just pseudo-random numbers otherwise. """ try: return os.urandom(length) except AttributeError: return b''.join([ bytes((random.randrange(256),)) for i in range(length)]) def secret_string(length=25): """ Returns a random string of the given length. The string is a base64-encoded version of a set of random bytes, truncated to the given length (and without any newlines). """ s = random_bytes(length) s = base64.b64encode(s) s = s.decode('ascii') for badchar in '\n\r=': s = s.replace(badchar, '') # We're wasting some characters here. But random characters are # cheap ;) return s[:length] pastescript-3.7.0/paste/script/wsgiutils_server.py000066400000000000000000000013551474421447600225150ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php from paste.script.serve import ensure_port_cleanup from paste.translogger import TransLogger def run_server(wsgi_app, global_conf, host='localhost', port=8080): import wsgiserver logged_app = TransLogger(wsgi_app) port = int(port) # For some reason this is problematic on this server: ensure_port_cleanup([(host, port)], maxtries=2, sleeptime=0.5) server = wsgiserver.WSGIServer(logged_app, host=host, port=port) logged_app.logger.info('Starting HTTP server on http://%s:%s', host, port) server.start() pastescript-3.7.0/regen-docs000077500000000000000000000003611474421447600160530ustar00rootroot00000000000000#!/bin/sh mkdir -p docs/_static docs/_build sphinx-build -E -b html docs/ docs/_build || exit 1 if [ "$1" = "publish" ] ; then cd docs/ echo "Uploading files..." scp -r _build/* ianb@webwareforpython.org:/home/paste/htdocs/script/ fi pastescript-3.7.0/scripts/000077500000000000000000000000001474421447600155665ustar00rootroot00000000000000pastescript-3.7.0/scripts/paster000077500000000000000000000004411474421447600170110ustar00rootroot00000000000000#!/usr/bin/env python import os import sys relative_paste = os.path.join( os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'paste') if os.path.exists(relative_paste): sys.path.insert(0, os.path.dirname(relative_paste)) from paste.script import command command.run() pastescript-3.7.0/setup.cfg000066400000000000000000000001351474421447600157170ustar00rootroot00000000000000[bdist_wheel] universal = 1 [nosetests] tests = tests [egg_info] tag_build = tag_date = 0 pastescript-3.7.0/setup.py000066400000000000000000000130651474421447600156160ustar00rootroot00000000000000# Procedure to release a new version: # # - run tests: run tox # - update version in setup.py (version) # - check that "python setup.py sdist" contains all files tracked by # the SCM (Mercurial): update MANIFEST.in if needed # - update changelog: docs/news.txt # # - increment version in setup.py (version) # - git commit # - git tag -s VERSION # - git push # - python setup.py sdist bdist_wheel upload --sign from setuptools import setup, find_packages import os import re import sys version = '3.7.0' news = os.path.join(os.path.dirname(__file__), 'docs', 'news.txt') found_news = '' if os.path.exists(news): with open(news) as fp: news = fp.read() parts = re.split(r'([0-9\.]+)\s*\n\r?-+\n\r?', news) for i in range(len(parts)-1): if parts[i] == version: found_news = parts[i+i] break if not found_news: print('Warning: no news for this version found', file=sys.stderr) with open("README.rst") as fp: long_description = fp.read().strip() if found_news: title = 'Changes in %s' % version long_description += "\n%s\n%s\n" % (title, '-'*len(title)) long_description += found_news setup( name="PasteScript", version=version, description="A pluggable command-line frontend, including commands to setup package file layouts", long_description=long_description, classifiers=[ "Development Status :: 6 - Mature", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules", "Framework :: Paste", ], keywords='web wsgi setuptools framework command-line setup', author="Ian Bicking", author_email="ianb@colorstudy.com", url="https://pastescript.readthedocs.io/", namespace_packages=['paste'], license='MIT', python_requires='>=3.8', packages=find_packages(exclude=['tests','tests.*']), package_data={ 'paste.script': ['paster-templates/basic_package/setup.*', 'paster-templates/basic_package/tests/*.py', # @@: docs/ doesn't have any files :( 'paster-templates/basic_package/+package+/*.py'], }, zip_safe=False, extras_require={ 'Templating': [], 'Cheetah': ['Cheetah'], 'Config': ['PasteDeploy'], 'WSGIUtils': ['WSGIserver'], 'Flup': ['Flup'], # the Paste feature means the complete set of features; # (other features are truly optional) 'Paste': ['PasteDeploy', 'Cheetah'], }, entry_points=""" [paste.global_paster_command] help=paste.script.help:HelpCommand create=paste.script.create_distro:CreateDistroCommand [Templating] serve=paste.script.serve:ServeCommand [Config] request=paste.script.request:RequestCommand [Config] post=paste.script.request:RequestCommand [Config] exe=paste.script.exe:ExeCommand points=paste.script.entrypoints:EntryPointCommand make-config=paste.script.appinstall:MakeConfigCommand setup-app=paste.script.appinstall:SetupCommand [paste.paster_command] grep = paste.script.grep:GrepCommand [paste.paster_create_template] basic_package=paste.script.templates:BasicPackage [paste.server_runner] wsgiutils=paste.script.wsgiutils_server:run_server [WSGIUtils] flup_ajp_thread=paste.script.flup_server:run_ajp_thread [Flup] flup_ajp_fork=paste.script.flup_server:run_ajp_fork [Flup] flup_fcgi_thread=paste.script.flup_server:run_fcgi_thread [Flup] flup_fcgi_fork=paste.script.flup_server:run_fcgi_fork [Flup] flup_scgi_thread=paste.script.flup_server:run_scgi_thread [Flup] flup_scgi_fork=paste.script.flup_server:run_scgi_fork [Flup] cgi=paste.script.cgi_server:paste_run_cgi cherrypy=paste.script.cherrypy_server:cpwsgi_server twisted=paste.script.twisted_web2_server:run_twisted [paste.app_factory] test=paste.script.testapp:make_test_application [paste.entry_point_description] paste.entry_point_description = paste.script.epdesc:MetaEntryPointDescription paste.paster_create_template = paste.script.epdesc:CreateTemplateDescription paste.paster_command = paste.script.epdesc:PasterCommandDescription paste.global_paster_command = paste.script.epdesc:GlobalPasterCommandDescription paste.app_install = paste.script.epdesc:AppInstallDescription # These aren't part of Paste Script particularly, but # we'll document them here console_scripts = paste.script.epdesc:ConsoleScriptsDescription # @@: Need non-console scripts... distutils.commands = paste.script.epdesc:DistutilsCommandsDescription distutils.setup_keywords = paste.script.epdesc:SetupKeywordsDescription egg_info.writers = paste.script.epdesc:EggInfoWriters # @@: Not sure what this does: #setuptools.file_finders = paste.script.epdesc:SetuptoolsFileFinders [console_scripts] paster=paste.script.command:run [distutils.setup_keywords] paster_plugins = setuptools.dist:assert_string_list [egg_info.writers] paster_plugins.txt = setuptools.command.egg_info:write_arg """, install_requires=[ 'Paste>=3.0', 'PasteDeploy', 'setuptools', ], ) pastescript-3.7.0/tests/000077500000000000000000000000001474421447600152415ustar00rootroot00000000000000pastescript-3.7.0/tests/__init__.py000066400000000000000000000016721474421447600173600ustar00rootroot00000000000000import sys import os import shutil import pkg_resources here = os.path.dirname(__file__) base = os.path.dirname(here) fake_packages = os.path.join(here, 'fake_packages') sys.path.append(fake_packages) sys.path.append(here) sys.path.insert(0, base) here = os.path.dirname(__file__) egg_info_dir = os.path.join(here, 'fake_packages', 'FakePlugin.egg', 'EGG-INFO') info_dir = os.path.join(here, 'fake_packages', 'FakePlugin.egg', 'FakePlugin.egg-info') if not os.path.exists(egg_info_dir): try: os.symlink(info_dir, egg_info_dir) except: shutil.copytree(info_dir, egg_info_dir) pkg_resources.working_set.add_entry(fake_packages) pkg_resources.working_set.add_entry(base) if not os.environ.get('PASTE_TESTING'): output_dir = os.path.join(here, 'appsetup', 'output') if os.path.exists(output_dir): shutil.rmtree(output_dir) pkg_resources.require('FakePlugin') pastescript-3.7.0/tests/appsetup/000077500000000000000000000000001474421447600171025ustar00rootroot00000000000000pastescript-3.7.0/tests/appsetup/__init__.py000066400000000000000000000000001474421447600212010ustar00rootroot00000000000000pastescript-3.7.0/tests/appsetup/test_make_project.py000066400000000000000000000121761474421447600231650ustar00rootroot00000000000000 import os from unittest import SkipTest from paste.fixture import TestFileEnvironment import pkg_resources for spec in ['PasteScript', 'Paste', 'PasteDeploy', 'PasteWebKit', 'ZPTKit']: try: pkg_resources.require(spec) except pkg_resources.DistributionNotFound as dnf: raise SkipTest(repr(dnf)) template_path = os.path.join( os.path.dirname(__file__), 'testfiles') test_environ = os.environ.copy() test_environ['PASTE_TESTING'] = 'true' testenv = TestFileEnvironment( os.path.join(os.path.dirname(__file__), 'output'), template_path=template_path, environ=test_environ) def svn_repos_setup(): res = testenv.run('svnadmin', 'create', 'REPOS', printresult=False) testenv.svn_url = 'file://' + testenv.base_path + '/REPOS' assert 'REPOS' in res.files_created testenv.ignore_paths.append('REPOS') def paster_create(): global projenv res = testenv.run('paster', 'create', '--verbose', '--svn-repository=' + testenv.svn_url, '--template=paste_deploy', '--template=webkit', '--template=zpt', '--no-interactive', 'ProjectName', 'version=0.1', 'author=Test Author', 'author_email=test@example.com') expect_fn = ['tests', 'docs', 'projectname', 'docs', 'setup.py', 'ProjectName.egg-info', ] for fn in expect_fn: fn = os.path.join('ProjectName', fn) assert fn in res.files_created assert fn in res.stdout setup = res.files_created['ProjectName/setup.py'] setup.mustcontain('test@example.com') setup.mustcontain('Test Author') setup.mustcontain('0.1') setup.mustcontain('projectname.wsgiapp:make_app') # ZPTKit should add this: setup.mustcontain("include_package_data") assert '0.1' in setup sitepage = res.files_created['ProjectName/projectname/sitepage.py'] proj_dir = os.path.join(testenv.cwd, 'ProjectName') testenv.run('svn commit -m "new project"', cwd=proj_dir) testenv.run('python setup.py egg_info', cwd=proj_dir, expect_stderr=True) testenv.run('svn', 'commit', '-m', 'Created project', 'ProjectName') # A new environment with a new projenv = TestFileEnvironment( os.path.join(testenv.base_path, 'ProjectName'), start_clear=False, template_path=template_path, environ=test_environ) projenv.environ['PYTHONPATH'] = ( projenv.environ.get('PYTHONPATH', '') + ':' + projenv.base_path) projenv.proj_dir = proj_dir def make_servlet(): res = projenv.run( 'paster servlet --verbose --simulate test1', cwd=projenv.proj_dir) assert not res.files_created and not res.files_updated res = projenv.run('paster servlet -vvv test1') assert 'projectname/web/test1.py' in res.files_created assert 'projectname/templates/test1.pt' in res.files_created res = projenv.run('paster servlet -vvv ack.test2') assert 'projectname/web/ack/test2.py' in res.files_created assert 'projectname/templates/ack/test2.pt' in res.files_created res = projenv.run('paster servlet --no-servlet -vvv test3') assert 'projectname/web/test3.py' not in res.files_created assert 'projectname/templates/test3.pt' in res.files_created res = projenv.run('svn status') # Make sure all files are added to the repository: assert '?' not in res.stdout def do_pytest(): res = projenv.run('py.test tests/', cwd=os.path.join(testenv.cwd, 'ProjectName'), expect_stderr=True) assert len(res.stderr.splitlines()) <= 1, ( "Too much info on stderr: %s" % res.stderr) def config_permissions(): projenv.writefile('ProjectName.egg-info/iscape.txt', frompath='iscape.txt') projenv.writefile('projectname/web/admin/index.py', frompath='admin_index.py') projenv.writefile('tests/test_forbidden.py', frompath='test_forbidden.py') res = projenv.run('py.test tests/test_forbidden.py', expect_stderr=True) assert len(res.stderr.splitlines()) <= 1, ( "Too much info on stderr: %s" % res.stderr) def make_tag(): global tagenv res = projenv.run('svn commit -m "updates"') res = projenv.run('python setup.py svntag --version=0.5') assert 'Tagging 0.5 version' in res.stdout assert 'Auto-update of version strings' in res.stdout res = testenv.run('svn co %s/ProjectName/tags/0.5 Proj-05' % testenv.svn_url) setup = res.files_created['Proj-05/setup.py'] setup.mustcontain('0.5') assert 'Proj-05/setup.cfg' not in res.files_created tagenv = TestFileEnvironment( os.path.join(testenv.base_path, 'Proj-05'), start_clear=False, template_path=template_path) def test_project(): global projenv projenv = None yield svn_repos_setup yield paster_create yield make_servlet yield do_pytest yield config_permissions yield make_tag pastescript-3.7.0/tests/appsetup/testfiles/000077500000000000000000000000001474421447600211045ustar00rootroot00000000000000pastescript-3.7.0/tests/appsetup/testfiles/admin_index.py000066400000000000000000000001401474421447600237300ustar00rootroot00000000000000from projectname.sitepage import SitePage class index(SitePage): link_id = 'admin_index' pastescript-3.7.0/tests/appsetup/testfiles/conftest.py000066400000000000000000000002711474421447600233030ustar00rootroot00000000000000# This disables py.test for this directory import py class DisableDirectory(py.test.collect.Directory): def buildname2items(self): return {} Directory = DisableDirectory pastescript-3.7.0/tests/appsetup/testfiles/iscape.txt000066400000000000000000000002571474421447600231150ustar00rootroot00000000000000[tool:projectname] name = ProjectName id = projectname roles = editor home = / admin_index.href = admin/ admin_index.title = ProjectName Admin admin_index.roles = admin pastescript-3.7.0/tests/appsetup/testfiles/test_forbidden.py000066400000000000000000000005111474421447600244460ustar00rootroot00000000000000from paste.fixture import TestApp def test_forbidden(): # Unfortunately, auth isn't enabled in the standard app # yet, so we ignore that return app = TestApp() # Of what? app.get('/') #app.get('/admin/', status=401) #app.get('/admin/', status=403, # extra_environ={'REMOTE_USER': 'bob'}) pastescript-3.7.0/tests/fake_packages/000077500000000000000000000000001474421447600200055ustar00rootroot00000000000000pastescript-3.7.0/tests/fake_packages/FakePlugin.egg/000077500000000000000000000000001474421447600225735ustar00rootroot00000000000000pastescript-3.7.0/tests/fake_packages/FakePlugin.egg/FakePlugin.egg-info/000077500000000000000000000000001474421447600263125ustar00rootroot00000000000000pastescript-3.7.0/tests/fake_packages/FakePlugin.egg/FakePlugin.egg-info/PKG-INFO000066400000000000000000000002661474421447600274130ustar00rootroot00000000000000Metadata-Version: 1.0 Name: FakePlugin Version: 0.1 Summary: UNKNOWN Home-page: UNKNOWN Author: UNKNOWN Author-email: UNKNOWN License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN pastescript-3.7.0/tests/fake_packages/FakePlugin.egg/FakePlugin.egg-info/entry_points.txt000066400000000000000000000001001474421447600315770ustar00rootroot00000000000000[paste.paster_command] testcom = fakeplugin.testcom:TestCommand pastescript-3.7.0/tests/fake_packages/FakePlugin.egg/FakePlugin.egg-info/paster_plugins.txt000066400000000000000000000000151474421447600321060ustar00rootroot00000000000000PasteScript pastescript-3.7.0/tests/fake_packages/FakePlugin.egg/FakePlugin.egg-info/top_level.txt000066400000000000000000000000131474421447600310360ustar00rootroot00000000000000fakeplugin pastescript-3.7.0/tests/fake_packages/FakePlugin.egg/fakeplugin/000077500000000000000000000000001474421447600247205ustar00rootroot00000000000000pastescript-3.7.0/tests/fake_packages/FakePlugin.egg/fakeplugin/__init__.py000066400000000000000000000000021474421447600270210ustar00rootroot00000000000000# pastescript-3.7.0/tests/fake_packages/FakePlugin.egg/setup.py000066400000000000000000000001741474421447600243070ustar00rootroot00000000000000from setuptools import setup, find_packages setup( name='FakePlugin', version='0.1', packages=find_packages()) pastescript-3.7.0/tests/sample_templates/000077500000000000000000000000001474421447600206005ustar00rootroot00000000000000pastescript-3.7.0/tests/sample_templates/test1.txt000066400000000000000000000001171474421447600224000ustar00rootroot00000000000000#def body(c, d): This shouldn't work, since it doesn't end with _tmpl #end def pastescript-3.7.0/tests/sample_templates/test2.py_tmpl000066400000000000000000000001731474421447600232500ustar00rootroot00000000000000#def body(a) This is a real template with a $a variable #end def #def inner(foo) This variable shouldn't be read #end def pastescript-3.7.0/tests/sample_templates/test3.ini_tmpl000066400000000000000000000000621474421447600233750ustar00rootroot00000000000000#def body(b=1) This method has a default #end def pastescript-3.7.0/tests/sample_templates/test4.html_tmpl000066400000000000000000000000451474421447600235640ustar00rootroot00000000000000 $title pastescript-3.7.0/tests/test_command.py000066400000000000000000000170501474421447600202730ustar00rootroot00000000000000#!/usr/bin/env python from paste.script import command from paste.script import create_distro from paste.script import entrypoints import contextlib import io import os import re import shutil import sys import tempfile import textwrap import unittest @contextlib.contextmanager def capture_stdout(): stdout = sys.stdout try: sys.stdout = io.StringIO() yield sys.stdout finally: sys.stdout = stdout @contextlib.contextmanager def temporary_dir(): old_dir = os.getcwd() try: if hasattr(tempfile, 'TemporaryDirectory'): # Python 3 with tempfile.TemporaryDirectory() as tmpdir: os.chdir(tmpdir) yield else: # Python 2 tmpdir = tempfile.mkdtemp() try: os.chdir(tmpdir) yield finally: shutil.rmtree(tmpdir) finally: os.chdir(old_dir) class CommandTest(unittest.TestCase): maxDiff = 1024 def test_help(self): usage = textwrap.dedent(''' Usage: [paster_options] COMMAND [command_options] Options: --version show program's version number and exit --plugin=PLUGINS Add a plugin to the list of commands (plugins are Egg specs; will also require() the Egg) -h, --help Show this help message 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 ''').strip() + "\n\n" with capture_stdout() as stdout: argv = sys.argv sys.argv = ['', '--help'] try: try: command.run(['--help']) except SystemExit as exc: self.assertEqual(exc.code, 0) else: self.fail("SystemExit not raised") finally: sys.argv = argv class CreateDistroCommandTest(unittest.TestCase): maxDiff = 1024 def setUp(self): self.cmd = create_distro.CreateDistroCommand('create_distro') def test_list_templates(self): templates = textwrap.dedent(''' Available templates: basic_package: A basic setuptools-enabled package paste_deploy: A web application deployed through paste.deploy ''').strip() + "\n" with capture_stdout() as stdout: self.cmd.run(['--list-templates']) self.assertEqual(templates, stdout.getvalue()) def test_basic_package(self): inputs = [ '1.0', # Version 'description', # Description 'long description', # Long description 'keyword1 keyword2', # Keywords 'author name', # Author name 'author@domain.com', # Author email 'http://example.com', # URL of homepage 'license', # License 'True', # zip_safe ] name = 'test' setup_cfg = textwrap.dedent(''' [egg_info] tag_build = dev tag_svn_revision = true ''').strip() + '\n' setup_py = textwrap.dedent(r''' from setuptools import setup, find_packages import sys, os version = '1.0' setup(name='test', version=version, description="description", long_description="""\ long description""", classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers keywords='keyword1 keyword2', author='author name', author_email='author@domain.com', url='http://example.com', license='license', packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), include_package_data=True, zip_safe=True, install_requires=[ # -*- Extra requirements: -*- ], entry_points=""" # -*- Entry points: -*- """, ) ''').strip() + "\n" with temporary_dir(): stdin = sys.stdin try: sys.stdin = io.StringIO('\n'.join(inputs)) with capture_stdout(): self.cmd.run(['--template=basic_package', name]) finally: sys.stdin = stdin os.chdir(name) with open("setup.cfg") as f: self.assertEqual(setup_cfg, f.read()) with open("setup.py") as f: self.assertEqual(setup_py, f.read()) with open(os.path.join(name, "__init__.py")) as f: self.assertEqual("#\n", f.read()) class EntryPointsTest(unittest.TestCase): maxDiff = 4096 def setUp(self): self.cmd = entrypoints.EntryPointCommand('entrypoint') def test_paster_command(self): # Issue #20: Check that SuperGeneric works on Python 3 paster = textwrap.dedent(''' create = paste.script.create_distro:CreateDistroCommand (self, name) exe = paste.script.exe:ExeCommand (self, name) help = paste.script.help:HelpCommand (self, name) make-config = paste.script.appinstall:MakeConfigCommand (self, name) points = paste.script.entrypoints:EntryPointCommand (self, name) post = paste.script.request:RequestCommand (self, name) request = paste.script.request:RequestCommand (self, name) serve = paste.script.serve:ServeCommand (self, name) setup-app = paste.script.appinstall:SetupCommand (self, name) ''').strip() with capture_stdout() as stdout: res = self.cmd.run(['paster_command']) self.assertEqual(res, 0) out = stdout.getvalue() self.assertIn(paster, out) class PostTest(unittest.TestCase): maxDiff = 4096 def test_post(self): config = os.path.join('docs', 'example_app.ini') url = '/' with capture_stdout() as stdout: stdout.buffer = io.BytesIO() try: command.run(['post', config, url]) except SystemExit as exc: self.assertEqual(exc.code, 0) else: self.fail("SystemExit not raised") out = stdout.buffer.getvalue() out = out.decode('utf-8') html_regex = textwrap.dedent(''' Test Application .* ''').strip() html_regex = '\n%s\n' % html_regex html_regex = re.compile(html_regex, re.DOTALL) self.assertRegex(out, html_regex) if __name__ == "__main__": unittest.main() pastescript-3.7.0/tests/test_egg_finder.py000066400000000000000000000015321474421447600207440ustar00rootroot00000000000000import os from paste.script import pluginlib def test_egg_info(): egg_dir = os.path.join(os.path.dirname(__file__), 'fake_packages', 'FakePlugin.egg') found = pluginlib.find_egg_info_dir(os.path.join(egg_dir, 'fakeplugin')) assert found == os.path.join(egg_dir, 'FakePlugin.egg-info') found = pluginlib.find_egg_info_dir(os.path.dirname(__file__)) assert found == os.path.join( os.path.dirname(os.path.dirname(__file__)), 'PasteScript.egg-info') def test_resolve_plugins(): plugins = ['FakePlugin'] all = pluginlib.resolve_plugins(plugins) assert all assert len(all) == 2 def test_find_commands(): all = pluginlib.resolve_plugins(['PasteScript', 'FakePlugin']) commands = pluginlib.load_commands_from_plugins(all) print(commands) assert 'testcom' in commands pastescript-3.7.0/tests/test_logging_config.py000066400000000000000000000134631474421447600216340ustar00rootroot00000000000000#!/usr/bin/env python # # Copyright 2001-2004 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, # provided that the above copyright notice appear in all copies and that # both that copyright notice and this permission notice appear in # supporting documentation, and that the name of Vinay Sajip # not be used in advertising or publicity pertaining to distribution # of the software without specific, written prior permission. # VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL # VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER # IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # This file is part of the Python logging distribution. See # http://www.red-dove.com/python_logging.html # """Test harness for the logging module. Run all tests. Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. """ import logging import os import sys import tempfile from paste.script.util import logging_config def message(s): sys.stdout.write("%s\n" % s) #---------------------------------------------------------------------------- # Test 4 #---------------------------------------------------------------------------- # config0 is a standard configuration. config0 = """ [loggers] keys=root [handlers] keys=hand1 [formatters] keys=form1 [logger_root] level=NOTSET handlers=hand1 [handler_hand1] class=StreamHandler level=NOTSET formatter=form1 args=(sys.stdout,) [formatter_form1] format=%(levelname)s:%(name)s:%(message)s datefmt= """ # config1 adds a little to the standard configuration. config1 = """ [loggers] keys=root,parser [handlers] keys=hand1, hand2 [formatters] keys=form1, form2 [logger_root] level=NOTSET handlers=hand1,hand2 [logger_parser] level=DEBUG handlers=hand1 propagate=1 qualname=compiler.parser [handler_hand1] class=StreamHandler level=NOTSET formatter=form1 args=(sys.stdout,) [handler_hand2] class=StreamHandler level=NOTSET formatter=form2 args=(sys.stderr,) [formatter_form1] format=%(levelname)s:%(name)s:%(message)s datefmt= [formatter_form2] format=:%(message)s datefmt= """ # config2 has a subtle configuration error that should be reported config2 = config1.replace("sys.stdout", "sys.stbout") # config3 has a less subtle configuration error config3 = config1.replace("formatter=form1", "formatter=misspelled_name") # config4: support custom Handler classes config4 = config1.replace("class=StreamHandler", "class=logging.StreamHandler") def test4(): for i in range(5): conf = globals()['config%d' % i] sys.stdout.write('config%d: ' % i) loggerDict = logging.getLogger().manager.loggerDict with logging._lock: saved_handlers = logging._handlers.copy() if hasattr(logging, '_handlerList'): saved_handler_list = logging._handlerList[:] saved_loggers = loggerDict.copy() try: fn = tempfile.mktemp(".ini") f = open(fn, "w") f.write(conf) f.close() try: logging_config.fileConfig(fn) #call again to make sure cleanup is correct logging_config.fileConfig(fn) except: if i not in (2, 3): raise t = sys.exc_info()[0] message(str(t) + ' (expected)') else: message('ok.') os.remove(fn) finally: with logging._lock: logging._handlers.clear() logging._handlers.update(saved_handlers) if hasattr(logging, '_handlerList'): logging._handlerList[:] = saved_handler_list loggerDict = logging.getLogger().manager.loggerDict loggerDict.clear() loggerDict.update(saved_loggers) #---------------------------------------------------------------------------- # Test 5 #---------------------------------------------------------------------------- test5_config = """ [loggers] keys=root [handlers] keys=hand1 [formatters] keys=form1 [logger_root] level=NOTSET handlers=hand1 [handler_hand1] class=StreamHandler level=NOTSET formatter=form1 args=(sys.stdout,) [formatter_form1] #class=test.test_logging.FriendlyFormatter class=test_logging_config.FriendlyFormatter format=%(levelname)s:%(name)s:%(message)s datefmt= """ class FriendlyFormatter (logging.Formatter): def formatException(self, ei): return "%s... Don't panic!" % str(ei[0]) def test5(): loggerDict = logging.getLogger().manager.loggerDict with logging._lock: saved_handlers = logging._handlers.copy() if hasattr(logging, '_handlerList'): saved_handler_list = logging._handlerList[:] saved_loggers = loggerDict.copy() try: fn = tempfile.mktemp(".ini") f = open(fn, "w") f.write(test5_config) f.close() logging_config.fileConfig(fn) try: raise KeyError except KeyError: logging.exception("just testing") os.remove(fn) hdlr = logging.getLogger().handlers[0] logging.getLogger().handlers.remove(hdlr) finally: with logging._lock: logging._handlers.clear() logging._handlers.update(saved_handlers) if hasattr(logging, '_handlerList'): logging._handlerList[:] = saved_handler_list loggerDict = logging.getLogger().manager.loggerDict loggerDict.clear() loggerDict.update(saved_loggers) pastescript-3.7.0/tests/test_plugin_adder.py000066400000000000000000000013401474421447600213050ustar00rootroot00000000000000import os from paste.script import pluginlib egg_dir = os.path.join(os.path.dirname(__file__), 'fake_packages', 'FakePlugin.egg') plugin_file = os.path.join(egg_dir, 'paster_plugins.txt') def plugin_lines(): if not os.path.exists(plugin_file): return [] f = open(plugin_file) lines = f.readlines() f.close() return [l.strip() for l in lines if l.strip()] def test_add_remove(): prev = plugin_lines() pluginlib.add_plugin(egg_dir, 'Test') assert 'Test' in plugin_lines() pluginlib.remove_plugin(egg_dir, 'Test') assert 'Test' not in plugin_lines() assert prev == plugin_lines() if not prev and os.path.exists(plugin_file): os.unlink(plugin_file) pastescript-3.7.0/tests/test_template_introspect.py000066400000000000000000000007531474421447600227440ustar00rootroot00000000000000from unittest import SkipTest import os from paste.script import templates tmpl_dir = os.path.join(os.path.dirname(__file__), 'sample_templates') def test_find(): try: import Cheetah.Template except ImportError: raise SkipTest("need Cheetah.Template") vars = templates.find_args_in_dir(tmpl_dir, True) assert 'a' in vars assert vars['a'].default is templates.NoDefault assert 'b' in vars assert vars['b'].default == 1 assert len(vars) == 2 pastescript-3.7.0/tests/test_util.py000066400000000000000000000006151474421447600176310ustar00rootroot00000000000000from paste.script.util import secret def test_random(): for length in (1, 10, 100): data = secret.random_bytes(length) assert isinstance(data, bytes) assert len(data) == length def test_secret_string(): data = secret.secret_string() assert isinstance(data, str) data = secret.secret_string(20) assert isinstance(data, str) assert len(data) == 20 pastescript-3.7.0/tox.ini000066400000000000000000000006311474421447600154120ustar00rootroot00000000000000# Tox (http://tox.testrun.org/) is a tool for running tests # in multiple virtualenvs. This configuration file will run the # test suite on all supported python versions. To use it, "pip install tox" # and then run "tox" from this directory. [tox] envlist = py{38,39,310,311,312,313}, pypy, pypy3 [testenv] deps = Paste>=1.3 PasteDeploy commands = python -m unittest discover tests {posargs:-v}