pax_global_header00006660000000000000000000000064121647561660014530gustar00rootroot0000000000000052 comment=cf0527f5ee1880ab6066d7bb83c899af42f5394a gunicorn-17.5/000077500000000000000000000000001216475616600133105ustar00rootroot00000000000000gunicorn-17.5/.gitignore000077500000000000000000000005461216475616600153100ustar00rootroot00000000000000*.gem *.sw* *.pyc *#* build dist setuptools-* .svn/* .DS_Store *.so .Python distribute-0.6.8-py2.6.egg distribute-0.6.8.tar.gz gunicorn.egg-info nohup.out .coverage doc/.sass-cache bin/ lib/ man/ include/ html/ examples/frameworks/pylonstest/PasteScript* examples/frameworks/pylonstest/pylonstest.egg-info/ examples/frameworks/django/testing/testdb.sql .tox gunicorn-17.5/.travis.yml000066400000000000000000000003321216475616600154170ustar00rootroot00000000000000language: python python: - "2.6" - "2.7" - "3.3" - "pypy" install: - "pip install -r requirements_dev.txt --use-mirrors" - "python setup.py install" script: py.test -x tests/ branches: only: - master gunicorn-17.5/CONTRIBUTING.md000066400000000000000000000154101216475616600155420ustar00rootroot00000000000000# Contributing to Gunicorn Want to hack on Gunicorn? Awesome! Here are instructions to get you started. They are probably not perfect, please let us know if anything feels wrong or incomplete. ## Contribution guidelines ### Pull requests are always welcome We are always thrilled to receive pull requests, and do our best to process them as fast as possible. Not sure if that typo is worth a pull request? Do it! We will appreciate it. If your pull request is not accepted on the first try, don't be discouraged! If there's a problem with the implementation, hopefully you received feedback on what to improve. We're trying very hard to keep Gunicorn lean and focused. We don't want it to do everything for everybody. This means that we might decide against incorporating a new feature. However, there might be a way to implement that feature *on top of* Gunicorn. ### Discuss your design on the mailing list We recommend discussing your plans [on the mailing list](http://gunicorn.org/#community) before starting to code - especially for more ambitious contributions. This gives other contributors a chance to point you in the right direction, give feedback on your design, and maybe point out if someone else is working on the same thing. ### Create issues... Any significant improvement should be documented as [a github issue](https://github.com/benoitc/gunicorn/issues) before anybody starts working on it. ### ...but check for existing issues first! Please take a moment to check that an issue doesn't already exist documenting your bug report or improvement proposal. If it does, it never hurts to add a quick "+1" or "I have this problem too". This will help prioritize the most common problems and requests. ### Conventions Fork the repo and make changes on your fork in a feature branch: - If it's a bugfix branch, name it XXX-something where XXX is the number of the issue - If it's a feature branch, create an enhancement issue to announce your intentions, and name it XXX-something where XXX is the number of the issue. Submit unit tests for your changes. Python has a great test framework built in; use it! Take a look at existing tests for inspiration. Run the full test suite on your branch before submitting a pull request. Make sure you include relevant updates or additions to documentation when creating or modifying features. Write clean code. Pull requests descriptions should be as clear as possible and include a reference to all the issues that they address. Code review comments may be added to your pull request. Discuss, then make the suggested modifications and push additional commits to your feature branch. Be sure to post a comment after pushing. The new commits will show up in the pull request automatically, but the reviewers will not be notified unless you comment. Before the pull request is merged, make sure that you squash your commits into logical units of work using `git rebase -i` and `git push -f`. After every commit the test suite should be passing. Include documentation changes in the same commit so that a revert would remove all traces of the feature or fix. Commits that fix or close an issue should include a reference like `Closes #XXX` or `Fixes #XXX`, which will automatically close the issue when merged. Add your name to the THANKS file, but make sure the list is sorted and your name and email address match your git configuration. The THANKS file is regenerated occasionally from the git commit history, so a mismatch may result in your changes being overwritten. ## Decision process ### How are decisions made? Short answer: with pull requests to the gunicorn repository. Gunicorn is an open-source project under the MIT License with an open design philosophy. This means that the repository is the source of truth for EVERY aspect of the project, including its philosophy, design, roadmap and APIs. *If it's part of the project, it's in the repo. It's in the repo, it's part of the project.* As a result, all decisions can be expressed as changes to the repository. An implementation change is a change to the source code. An API change is a change to the API specification. A philosophy change is a change to the relevant documentation. And so on. All decisions affecting gunicorn, big and small, follow the same 3 steps: * Step 1: Open a pull request. Anyone can do this. * Step 2: Discuss the pull request. Anyone can do this. * Step 3: Accept or refuse a pull request. The relevant maintainer does this (see below "Who decides what?") ### Who decides what? So all decisions are pull requests, and the relevant maintainer makes the decision by accepting or refusing the pull request. But how do we identify the relevant maintainer for a given pull request? Gunicorn follows the timeless, highly efficient and totally unfair system known as [Benevolent dictator for life](http://en.wikipedia.org/wiki/Benevolent_Dictator_for_Life), with Benoit Chesneau (aka benoitc), in the role of BDFL. This means that all decisions are made by default by me. Since making every decision myself would be highly unscalable, in practice decisions are spread across multiple maintainers. The relevant maintainer for a pull request is assigned in 3 steps: * Step 1: Determine the subdirectory affected by the pull request. This might be src/registry, docs/source/api, or any other part of the repo. * Step 2: Find the MAINTAINERS file which affects this directory. If the directory itself does not have a MAINTAINERS file, work your way up the the repo hierarchy until you find one. * Step 3: The first maintainer listed is the primary maintainer. The pull request is assigned to him. He may assign it to other listed maintainers, at his discretion. ### I'm a maintainer, should I make pull requests too? Primary maintainers are not required to create pull requests when changing their own subdirectory, but secondary maintainers are. ### Who assigns maintainers? benoitc. ### How can I become a maintainer? * Step 1: learn the component inside out * Step 2: make yourself useful by contributing code, bugfixes, support etc. * Step 3: volunteer on the irc channel (#gunicorn@freenode) Don't forget: being a maintainer is a time investment. Make sure you will have time to make yourself available. You don't have to be a maintainer to make a difference on the project! ### What are a maintainer's responsibility? It is every maintainer's responsibility to: * 1) Expose a clear roadmap for improving their component. * 2) Deliver prompt feedback and decisions on pull requests. * 3) Be available to anyone with questions, bug reports, criticism etc. on their component. This includes irc, github requests and the mailing list. * 4) Make sure their component respects the philosophy, design and roadmap of the project. ### How is this process changed? Just like everything else: by making a pull request :) gunicorn-17.5/LICENSE000066400000000000000000000021601216475616600143140ustar00rootroot000000000000002009-2013 (c) Benoît Chesneau 2009-2013 (c) Paul J. Davis 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. gunicorn-17.5/MAINTAINERS000066400000000000000000000003101216475616600147770ustar00rootroot00000000000000Benoit Chesneau Paul J. Davis Randall Leeds Konstantin Kapustin Kenneth Reitz gunicorn-17.5/MANIFEST.in000066400000000000000000000003511216475616600150450ustar00rootroot00000000000000include .gitignore include LICENSE include NOTICE include README.rst include THANKS include requirements_dev.txt recursive-include tests * recursive-include examples * recursive-include docs * recursive-include examples/frameworks * gunicorn-17.5/Makefile000066400000000000000000000003621216475616600147510ustar00rootroot00000000000000build: virtualenv --no-site-packages . bin/python setup.py develop bin/pip install -r requirements_dev.txt test: bin/python setup.py test coverage: bin/python setup.py test --cov clean: @rm -rf .Python bin lib include man build html gunicorn-17.5/NOTICE000066400000000000000000000102001216475616600142050ustar00rootroot00000000000000Gunicorn 2009-2013 (c) Benoît Chesneau 2009-2013 (c) Paul J. Davis Gunicorn is released under the MIT license. See the LICENSE file for the complete license. gunicorn.logging_config ----------------------- 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, INCLUDINGALL 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. gunicorn.debug -------------- Based on eventlet.debug module under MIT license: Unless otherwise noted, the files in Eventlet are under the following MIT license: Copyright (c) 2005-2006, Bob Ippolito Copyright (c) 2007-2010, Linden Research, Inc. Copyright (c) 2008-2010, Eventlet Contributors (see Eventlet AUTHORS) 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. doc/sitemap_gen.py ------------------ Under BSD License : Copyright (c) 2004, 2005, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. util/unlink.py -------------- backport frop python3 Lib/test/support.py gunicorn-17.5/README.rst000066400000000000000000000137521216475616600150070ustar00rootroot00000000000000About ----- Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server for UNIX. It's a pre-fork worker model ported from Ruby's Unicorn_ project. The Gunicorn server is broadly compatible with various web frameworks, simply implemented, light on server resource usage, and fairly speedy. Feel free to join us in `#gunicorn`_ on freenode_. .. image:: https://secure.travis-ci.org/benoitc/gunicorn.png?branch=master :alt: Build Status :target: https://travis-ci.org/benoitc/gunicorn Documentation ------------- http://docs.gunicorn.org Installation ------------ Gunicorn requires **Python 2.x >= 2.6** or **Python 3.x >= 3.1**. Install from sources:: $ python setup.py install Or from Pypi:: $ easy_install -U gunicorn You may also want to install Eventlet_ or Gevent_ if you expect that your application code may need to pause for extended periods of time during request processing. Check out the FAQ_ for more information on when you'll want to consider one of the alternate worker types. To install eventlet:: $ easy_install -U eventlet If you encounter errors when compiling the extensions for Eventlet_ or Gevent_ you most likely need to install a newer version of libev_ or libevent_. Basic Usage ----------- After installing Gunicorn you will have access to three command line scripts that can be used for serving the various supported web frameworks: ``gunicorn``, ``gunicorn_django``, and ``gunicorn_paster``. Commonly Used Arguments +++++++++++++++++++++++ * ``-c CONFIG, --config=CONFIG`` - Specify the path to a `config file`_ * ``-b BIND, --bind=BIND`` - Specify a server socket to bind. Server sockets can be any of ``$(HOST)``, ``$(HOST):$(PORT)``, or ``unix:$(PATH)``. An IP is a valid ``$(HOST)``. * ``-w WORKERS, --workers=WORKERS`` - The number of worker processes. This number should generally be between 2-4 workers per core in the server. Check the FAQ_ for ideas on tuning this parameter. * ``-k WORKERCLASS, --worker-class=WORKERCLASS`` - The type of worker process to run. You'll definitely want to read the `production page`_ for the implications of this parameter. You can set this to ``egg:gunicorn#$(NAME)`` where ``$(NAME)`` is one of ``sync``, ``eventlet``, ``gevent``, or ``tornado``. ``sync`` is the default. * ``-n APP_NAME, --name=APP_NAME`` - If setproctitle_ is installed you can adjust the name of Gunicorn process as they appear in the process system table (which affects tools like ``ps`` and ``top``). sync=gunicorn.workers.sync:SyncWorker eventlet=gunicorn.workers.geventlet:EventletWorker gevent=gunicorn.workers.ggevent:GeventWorker tornado There are various other parameters that affect user privileges, logging, etc. You can see the complete list with the expected:: $ gunicorn -h gunicorn ++++++++ The first and most basic script is used to serve 'bare' WSGI applications that don't require a translation layer. Basic usage:: $ gunicorn [OPTIONS] APP_MODULE Where ``APP_MODULE`` is of the pattern ``$(MODULE_NAME):$(VARIABLE_NAME)``. The module name can be a full dotted path. The variable name refers to a WSGI callable that should be found in the specified module. Example with test app:: $ cd examples $ gunicorn --workers=2 test:app gunicorn_django +++++++++++++++ You might not have guessed it, but this script is used to serve Django applications. Basic usage:: $ gunicorn_django [OPTIONS] [SETTINGS_PATH] By default ``SETTINGS_PATH`` will look for ``settings.py`` in the current directory. Example with your Django project:: $ cd path/to/yourdjangoproject $ gunicorn_django --workers=2 Alternatively, you can install some Gunicorn magic directly into your Django project and use the provided command for running the server. First you'll need to add ``gunicorn`` to your ``INSTALLED_APPS`` in the settings file:: INSTALLED_APPS = ( ... "gunicorn", ) Then you can run:: python manage.py run_gunicorn gunicorn_paster +++++++++++++++ Yeah, for Paster-compatible frameworks (Pylons, TurboGears 2, ...). We apologize for the lack of script name creativity. And some usage:: $ gunicorn_paster [OPTIONS] paste_config.ini Simple example:: $ cd yourpasteproject $ gunicorn_paster --workers=2 development.ini If you're wanting to keep on keeping on with the usual paster serve command, you can specify the Gunicorn server settings in your configuration file:: [server:main] use = egg:gunicorn#main host = 127.0.0.1 port = 5000 And then as per usual:: $ cd yourpasteproject $ paster serve development.ini workers=2 **Gunicorn paster from script** If you'd like to run Gunicorn paster from a script instead of the command line (for example: a runapp.py to start a Pyramid app), you can use this example to help get you started:: import os import multiprocessing from paste.deploy import appconfig, loadapp from gunicorn.app.pasterapp import paste_server if __name__ == "__main__": iniFile = 'config:development.ini' port = int(os.environ.get("PORT", 5000)) workers = multiprocessing.cpu_count() * 2 + 1 worker_class = 'gevent' app = loadapp(iniFile, relative_to='.') paste_server(app, host='0.0.0.0', port=port, workers=workers, worker_class=worker_class) LICENSE ------- Gunicorn is released under the MIT License. See the LICENSE_ file for more details. .. _Unicorn: http://unicorn.bogomips.org/ .. _`#gunicorn`: http://webchat.freenode.net/?channels=gunicorn .. _freenode: http://freenode.net .. _Eventlet: http://eventlet.net .. _Gevent: http://gevent.org .. _FAQ: http://docs.gunicorn.org/en/latest/faq.html .. _libev: http://software.schmorp.de/pkg/libev.html .. _libevent: http://monkey.org/~provos/libevent .. _`production page`: http://docs.gunicorn.org/en/latest/deploy.html .. _`config file`: http://docs.gunicorn.org/en/latest/configure.html .. _setproctitle: http://pypi.python.org/pypi/setproctitle/ .. _LICENSE: http://github.com/benoitc/gunicorn/blob/master/LICENSE gunicorn-17.5/THANKS000066400000000000000000000046631216475616600142340ustar00rootroot00000000000000Gunicorn THANKS =============== A number of people have contributed to Gunicorn by reporting problems, suggesting improvements or submitting changes. Some of these people are: Curt Micol Eric Florenzano Johan Bergström Xavier Grangier Sergey Shepelev Chris Dent Matt Good Randall Leeds thomasst Jonas Borgström PA Parent Travis Cline Adrien Lemaire Oliver Tonnhofer Anand Chitipothu Jannis Leidel Josh Ourisman Chris Lamb Neil Chintomby Alex Robbins Graham Dumpleton Dan Sully Fabian Topfstedt Denis Bilenko Michael Schurter Masahiro Nakagawa Denis Bilenko Phil Schanely Evan Mezeske Darii Denis Dan Callaghan Kristian Glass Mazdak Rezvani Maxim Kamenkov Konstantin Kapustin Djoume Salvetti ZheFu Peng (CMGS) Caleb Brown Marc Abramowitz Vangelis Koukis Prateek Singh Paudel Andrew Gorcester Kenneth Reitz Eric Shull Christos Stavrakakis Dan Callaghan sib David Vincelli Adnane Belmadiaf Dan Callaghan Greg McGuire Kevin Luikens Chris Streeter Jean-Philippe Serafin Krzysztof Urbaniak Alexandre Zani Andreas StÃŒhrk Philip Cristiano Jorge Niedbalski R Moriyoshi Koizumi gunicorn-17.5/docs/000077500000000000000000000000001216475616600142405ustar00rootroot00000000000000gunicorn-17.5/docs/Makefile000066400000000000000000000127241216475616600157060ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = ../bin/sphinx-build PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Gunicorn.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Gunicorn.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Gunicorn" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Gunicorn" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." gunicorn-17.5/docs/README.rst000066400000000000000000000004421216475616600157270ustar00rootroot00000000000000Generate Documentation ====================== Requirements ------------ To generate documentation you need to install: - Python >= 2.5 - Sphinx (http://sphinx.pocoo.org/) Generate html ------------- :: $ make html The command generates html document inside ``build/html`` dir. gunicorn-17.5/docs/gunicorn_ext.py000077500000000000000000000030271216475616600173230ustar00rootroot00000000000000import os import inspect import gunicorn.config as guncfg HEAD = """ Settings ======== This is an exhaustive list of settings for Gunicorn. Some settings are only able to be set from a configuration file. The setting name is what should be used in the configuration file. The command line arguments are listed as well for reference on setting at the command line. """ def format_settings(app): settings_file = os.path.join(app.srcdir, "settings.rst") ret = [] for i, s in enumerate(guncfg.KNOWN_SETTINGS): if i == 0 or s.section != guncfg.KNOWN_SETTINGS[i - 1].section: ret.append("%s\n%s\n\n" % (s.section, "-" * len(s.section))) ret.append(fmt_setting(s)) with open(settings_file, 'w') as settings: settings.write(HEAD) settings.write(''.join(ret)) def fmt_setting(s): if callable(s.default): val = inspect.getsource(s.default) val = "\n".join(" %s" % l for l in val.splitlines()) val = " ::\n\n" + val else: val = "``%s``" % s.default if s.cli and s.meta: args = ["%s %s" % (arg, s.meta) for arg in s.cli] cli = ', '.join(args) elif s.cli: cli = ", ".join(s.cli) out = [] out.append("%s" % s.name) out.append("~" * len(s.name)) out.append("") if s.cli: out.append("* ``%s``" % cli) out.append("* %s" % val) out.append("") out.append(s.desc) out.append("") out.append("") return "\n".join(out) def setup(app): app.connect('builder-inited', format_settings) gunicorn-17.5/docs/logo/000077500000000000000000000000001216475616600152005ustar00rootroot00000000000000gunicorn-17.5/docs/logo/gunicorn.png000066400000000000000000000520561216475616600175420ustar00rootroot00000000000000PNG  IHDR :=bKGD pHYs B(xtIME  5a} IDATxyx3#rb';RZ@-[3N( [KYm@JKKq t/;E@ll&P@[--[:N,[9,fFIh 52 ZVل>f^V9}yAAgέ" # 6&Gz=̴Whxfq?AA2}ê&)&eʷ5 t)̏Jqԩ@su<`ODD9   $ ̈́l:HO5cx + XSAALo>E׀J|*qK% Xbhzz_ցٙ|  "GMUs44*^DWvEMs#EWqUAAZRV \ ۙ1>{ݦ~h~A9G뉹cN]a I(T:88݈#{Vʉ @93V*&URUpXA LFUK y] d22h-ڸ$'>0>Ate!v |]ק8p`[a^f~$ɗ:::k !?Y5o``ͺ |(@pw PTVV&Lp 3cf^(3aѢEHo y-7?e\]ouBd̙{y Fm$2sK4} #A!?|ތC0gΉ5 `0X(e̬P=XŷȪ ׂ3UW_/~3Άą|FӴ0ӓAVF9=mrɪqj(紵IRA!9455)/Ny[w+{lRc=IsFd?F.~%A䟱W(*c ]?WrMX`v5f-3|4M󄶶s#|ENcBPq9IkMRAAe`|au)7NҥK/5lx:%"f0!MMMxS244t>Cl?3_,YGW/C!dY] y *ނ4mԡ?L?#Hݍ]f"Гně}IS 򬒰 _+*As_>5?Γn"cR9PίʑbQAAU,&nuBmmmOfKJJ6˒|~wY1>'ljEAyV!&pߖ* ljE#n9uJm)+"`qE|P+sv.gTϫsNgMR`>/#9Ei,rWQE/ <'9v|YUD-zzB'gg'/$N;?iڥ(t*XTO~ǐ lMD\]fڎg9,X^T0']*0a"cbQAAUD7Z{%lH$t8~Բ3܌Trx<`q/EAy%|RbU dib|e2sFc_n|x!H_sb>{Ģ |>7 g-Hc4 ]ǧ1|~<88ub%AAIL'%>a$ѵDb9e$H X3欎ײpW0ciĠaן&L$7YdеT41>("ȅQMKSAA}T1[Dcl}m[AH3ز^kYLbz%GkZCLS9T!4PK1'Eoτ>Wa:7\MPnhxF1 ͉J4T^ r"AD/DoN T6 PGcc 2 5 n0b/S[[[(+nmY֤,kPp+ N~y:l~D^+~(ב"U[xk= 40.@]H!1㺮)sNg7hVl'1D0rUKlEQrCCPi۱>B<)`dC\RܳpR/`dUڋLu .0 ISEYRWWwh6&Fq"ju~MMMi713lbfo* 9t6v^ liG̗tae Maz68`'&DQ7888=Mq:yF2&TnȢL6+T #%`np[L_N3fpe:c`,`0xR*o6Onmz{{pE@fk[8>H=NsB@P9pu!6>pKb.Mq[d(_;Eeߪ/J slvjIIbEymmm=,RNY:/G1v^sǢ %Xc._7u~*q@20jnntk\kjjr} 4m{`'yX$sMMMJ__߽0p}(̸ (ʣJreDԮzu;NG,+Z )P|C"G{qG4l7s9+LXHffMaҥ'mt:\$d3g!Ԕ 0CCC|뺾?(ˡ0Úe2nH>9y'&6"{BiSp)9F p-Iaf'c _?# AAs@ooo+%.ӨiZCׇBsR-sG l23jk"]@B.l،g/DndSH퀿z^cƋ@ 8d]ˉ!x?XѱKu  {{{/& = p<8E난?+[zg\*~7X}}}S655)>3V\ӘD,3:?[p>j;ڋF`A) {3b"HF&ӵw2Qk|˶XUֹ|6gocކaL&w%2̋5~ kgMAps`B+'::#<8؅gj |s78!a"8ۉz Zh6@=p*xO{`p] OٴE}}}1DRRRp8<0q7v7Qf~_ FiJDQ>{_ 6X(Je[[>TYYr/,M<`&sBCCC1\ƝE)A"Dž 6ܪ:ĩɪ(1 盓gP(T[R mY։Xlx=.fƢhc XUU#wA{ g~^8^3 ϟFO̙30MfyʲKǖ8uf৆alqD m]~z37C8%ta I5ʮfc~4͆X,vKKGDy' Yq^ Bhh{{{3_墠84ͿՍO]zI1̕h依vIۚߐxr{}H-~?0~9Z1)Z[[3 AQi#3 8ib<5ءթI߉[҈H0a˅,a'z0 c")51Uf>r& wh@ j0fWϫ*Fћ9ղnMӞ4@ 0as?Yu1ߘ٩UKƨ8/;&"Dt[ryO/e,BQj0#7d2y ݹ&&": <νeT}˲Fkap,ަ}3Z1=>,D"CaF갗)ZKFqSeS崞d{K.݅Ouo4>$9Mkt?%Se'd$i8F=,Y3׶;ihn3u%/efo F"[_0,˺Wp\{{2ft̙Ǜ 6R ^H$h~~i4IvAV]%dEXzn!4J$` P83/+E{yKG"~:(;z&\]]H$r/5;TXHЮui64a,soQ3}|j}e4aƕ_,'ARdy\S7Lo>ņ èp\?Jch‘<DDAhϰK~A(*M 1s]&CR f5L q[3:::rafRƕh.wxxh4zi:yL&@\TT~.DlW x*_|qy"Lޙ:+beYǹX7s<4M[r7nw+iRF a! ~ddrmzdiqt4]:Q=scʕ+Wf>,sIi=3]Ԧm'8^tѢE:]͇iwg6} 9c .:'Q!]E?o D"L*籐BU7Ij)e ah6lٖzG05T.`+nr\Ap@36)y5 E}}}b$ox700P\\\<ٲ<@9QeY _Q~˲2s}Gq;vrBxb9+1?]^t$l x<[׫LkwօO mI6x/ *- < `(~D"7w nr0'dz,Ƥ4#XHyr<ijdb[kCPq< @(/L&AvM[Ȋ+]^IAz3+6l<|o 1O<1W|dA̠"ƅR0(t)L=c +ͼX^!#ƶRQ FmuM0s"}'Ha>o]>=KHӴ˲`/7n9ҳ#U"ȿ;Bm$6l1gNW+EEE";kT JnBgy;0ĝ8b n @>1Ks<3fj.OyfJury<'3ݬXbll,k|zTUn&b/8ɳKJJ<9 Oyէx1—7CȅO~LN[m.Ά &LO,˲#ȷqNlGFEqM4 RylнYJ&]C)UUU7;.9N{<A>FjU7A~ J\yvS1$ KY1e1seН l+vX5 aSkڣׯn}UwEQ*<x=99<9 %-E5 p$Y%bC ׀f۷  :tF(*+p ~ns=}OH}1s7aqnT^݃$|n a'< ^Z>5-5樅K*i53gܻY(*BePzNnԉў2dWLu]'kYfڿc dcV8鎓W#gK-E?r0(AZfVJR*r aEwEEEsQw]ׯh6Ω2h"~ihwm5"OrORҟ(54\4$[m] BCA\dL^-qIeeevPh"ty1 cgmS5M&4fQDbI)GS'B0v'AS\&b !Mw(65{٢XۉQe߂ZbkNGtapp8Dg;(M.ʐ?bO! ϶[E>b...D:-"'0Qsgj [4W_,`k^*X} wRO 'B`"Z-Nu~3\RRVZ"f=$=L6"|4Gpiv(R5M)g}k7Ųy<0sv[گe=n0y"=Gj iL q#qhV@`fzȾZ>ز߭..R>ac "sGGt]d5M3wV\/{8PEy>+ܖ] v%#"ȽEUKR _maVusj٤x*8D `5B(1z?31T:tmH`oC+ CDO{ 7 AjЬYF_YY c$+U##H= 2󛚦] &*(9 4,%MI[)"zNӴәM4ODtFd_{? <=Z631h&"VIJ9=xOfN'26PۓCs(axl~@%jMWw3hv3kDt!68%]]bP*xM2Dh▃ΊT1L5rxh`iCCCwwwmmmK4M{#Ep>3GӴntƢheo[u]}aoNj 5f!_cRyW"Ngx!PW}v:0%B C?Xrܵf{dTX@9>낼M-C-B r04U;$.,Ҿ1 7ZUUn>a<0yNp] iDOf~_jU IvN܈i4\5?4m@}E%ӽIT6Uԛ2|>0|%عwGc]b;;5tN qI>W$<;l2~o0\?Xr0;"> B_tpq̼3W`$ɼV XUzƣ- i|*.Hw%]A^4mLm x.ڻTeb"=E鎥oneYĮ|y̤_™U=sm`SnOP_?_.w^_0޵Z5_hLnQkI; yش"ZƘu/3/΃!a"=\~^XC':.X5RŽ,΋:$RnE5w4:>KD"3ja|)' ME}"鹉̳f]DHg`-**:97~b XMqeQQ7q5-q>t|N |F <ٿGD?"KwdmX5o/csbx wSIm.s4iGxYᴨ&R9tο~O+I^ɓ0[ҽxѢE*p5iīE{gP>ߧsN͑#8_\Hrv%@oc8Pi5Uգj66\掎***]w /?k֬/e Dt.p8, X>n㝇6\%XE˙`W⺓+7[ngc\^{sk_g#=g3>E/{ &KUoxKKD9MMMox*GzhFȗl4 #*[@/)[7?. :ї:{sTZ 5[ ',t#]hRK}vyW!η.pk< &ܼ[c3_d^i|@ld_@kkP(4#gN[d2wttC9 O QU)^$0qFH ]׿̭I&gbcs[!'b+Clw_`9|uj[s{>J~.lf[U·Ax֔芊\o]׫_gF)_pd4+m0 ca0 Af층5~dmwQ嵛bh4ZODAlX6xɄ@kʔ)clƕ'r0*E ,'anEp~#NϕRUe<9N |?;~u}i 9`Q^֝zݫ2 d2y,\"'wpx0YF83_Df;N>H=L̷f6f9c#ȫaʲ- ."j$Fy.AB>l3uȢf.Q`J :FSb610ka'f455yBe{k#Cbؐa0nVUT&c-yX4ABFˉh0+od2$/>a[(rt$DDp_yBbm] ]}dށkWsN _ 4W'O1b_$`< $r~?p[`\ӒSx&핦i5Ko6 \lW^^~<܉1!%[}" (wN"0 i21cCmQZI͟f#ȿ\^[[S"pH$؃=D"aMڙԙÉD# 2h4gt/\ɻT; @stz1H: |"dKv*Z]#َrMEKE<@mT&VZ3|Ic4XcU4~?Ɨ½h*1p BPi<0*y11LDo29VZZ.' 3g424ƧH^g===e'8")~K Hx#JtE!v(%Ks:OuZɉf;O5wպocO䶋Vr)SăiNl:~\w4M{7}H$reBׯ?LUݙy23O&&̼ZQ̼5u7ϟ lD({؆y0`=wH$qi.MvUe?4'2|W1rjEYmYևUAAᅬ FE.U-T5r`Ν<-ȧbjQ=L֧nOMKMz@UW9]qqr̘7jDyOlU% m6k+*H$:ß|ss]xiAnYZ+`d5YCb!o2l2_- [Êa@V3YY\ϝ+R;vj%>ǣ C^@K`~ ( -s?pW ARX.mʥiGxcD-Z<xpk  B^PpYVJ&tu*AbtS4/˲=iV|;:TNIIAAajƪ#QX}VbS1%A0³=TK4A/QL@@e<[vzti&QW8  l輏YX٪5kkz8<&66 &ۘcYVCE)-AyOHNG@u4v0] 2;UT AQlVL#"[P>;!ߩX$-Ay`'=. z,k`Qal7`>^qD'f|_^',A+t:!\[DE=lCqQB鿰*Ai_ ^qo͊5SsY,kL*++;y4AIIɯ  wbGXa[Wn0Ȫ,Q1U-US<%bpZǿ9=(+NCD ʘZ'ldAA^t4t,at(uwd`&'iXc5/>I:UŮ Yġf꺞Ӈ% ]`<DdNAEE1Mk MK1_2<[V' ˚A](эܟޅ{.??h^wȆbsMNdfT3e2* /H/l]ٱc>t豎9]VMm31>un@YS'<`) ?(<+s555xϦ/FAAA^ v4v~WbT)hb*f\"JJU6 a%QT bJk-L&sD O͕WUUMRU5 `[EDXAsNL8[+_(f]-Ӳ1!+h!]A$[r3>SAD@ 0x}m^QQ=}AD LslSbl3&특dLYѯDimdYO: מQ'uJCYLD4M;ګu0 l":CNA(,|b=55-Ug2+7#9PDzI"W<ӰC [ Xn[\^bϞ={Ԣ\U$<0Lwχ0|CExZk8zIjv3`+܈>df-AUHLL===࠺6(CCCࠢ(oV,^W˲a5L*`>wfV@Q7nY&|>:44Y>6OMu[]!7ͱ/&_e1R'>6" ֘n,9]wYH$,"R6T]ٲ,Ӳ,SQ(9<<%%%xoigBcƬYK$&(MEQ>WAyNpB;:2ofw%_hIVEHMB]@mPyjHAJXDBBB1H'ġEiӛKnڵ|<ǘr#vvg/7e˖-Z)ڔsNDZ^\bQZk2c20V`ui?>;XW"w`FMN tԹ//߿syReUJYNDsN Ȝs✳A,܂̜ks眍8okkˆҡ;36Cu::;::Zhsd)iιQpsan(f9}[iKT[lFUTo7 " @D$"EZ#"$I0 q |1S&ץMc~y=<=/J'x<E+ED]i6 y]kIHM}𩆎v8P.h0e*5 |Yg70ߑ v8rH `Z{NrCCC>$9T*Ux< >;(lڃ]דW뎎"*V܊|uD(ytm{JD"[)ulRyp%8}uJ^=sY7!.kdfFW!PzKF#'e%";yf&*/ˮgZT߉HcJ ߿kDS7yqQ|~M쌉脈1c###3x< ygr%˲TmeKmo7f/{G?՛8if!ofD$QJ֞|׺jQ%"7 +tIx<z %~kV*EQTsfNa|cћ7N_?}x}*ˊku--\@mrjLBQxn" I1ιR*>w\QӳM)yHD{\pRDN8:K^=B?d|h֭, vyQHD:X'R/䛫ⅼq09gh qb1I4ihzwsn]KD[D\[#0s2:<dz)|%>vttj5d搈<ϣy9ɋ{!҄\Dr3; fy L$&󬵵KD~:[x image/svg+xml g gunicorn-17.5/docs/make.bat000066400000000000000000000117651216475616600156570ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source set I18NSPHINXOPTS=%SPHINXOPTS% source if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Gunicorn.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Gunicorn.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end gunicorn-17.5/docs/site/000077500000000000000000000000001216475616600152045ustar00rootroot00000000000000gunicorn-17.5/docs/site/.nojekyll000066400000000000000000000000001216475616600170220ustar00rootroot00000000000000gunicorn-17.5/docs/site/CNAME000066400000000000000000000000151216475616600157460ustar00rootroot00000000000000gunicorn.org gunicorn-17.5/docs/site/community.html000066400000000000000000000005241216475616600201170ustar00rootroot00000000000000 Green Unicorn - Community

Redirecting to here

gunicorn-17.5/docs/site/configuration.html000066400000000000000000000004601216475616600207410ustar00rootroot00000000000000 Green Unicorn - Configuration

Redirecting to here

gunicorn-17.5/docs/site/configure.html000066400000000000000000000004541216475616600200560ustar00rootroot00000000000000 Green Unicorn - Configure

Redirecting to here

gunicorn-17.5/docs/site/css/000077500000000000000000000000001216475616600157745ustar00rootroot00000000000000gunicorn-17.5/docs/site/css/style.css000066400000000000000000000150511216475616600176500ustar00rootroot00000000000000html,body { margin: 0; padding: 0; } h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,cite, code,del,dfn,em,img,q,s,samp,small,strike,strong,sub,sup,tt,var, dd,dl,dt,li,ol,ul,fieldset,form,label,legend,button,table,caption, tbody,tfoot,thead,tr,th,td { margin: 0; padding: 0; border: 0; font: inherit; vertical-align: baseline; } ol,ul { list-style: none; } html { overflow-y: scroll; font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } a:hover, a:active, a:focus { outline: 0; } img { border: 0; -ms-interpolation-mode: bicubic; } body { background: #F8F8F3; margin: 0; font: 14px/1.4 "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, "Lucida Grande", sans-serif; color: #67686B; height: auto; } a, a:hover { text-decoration: none; } .clearall { clear: both; display: block; overflow: hidden; visibility: hidden; width: 0; height: 0; } .logo-wrapper { border-bottom: 1px solid #2A8729; } .latest { width: 150px; top: 0; display: block; float: right; font-weight: bold; } .logo-div { width: 1000px; margin: 0 auto; padding: 5px; height: 72px; } .logo { width: 250px; margin: 0 auto; height: 119px; background: url(../images/logo-bottom.png) no-repeat bottom center; position: relative; z-index: 99999; } .banner-wrapper { background: url(../images/banner-bg.jpg) repeat; display: block; width: 100%; min-height: 365px; margin-top: 1px; margin-bottom: 1px; } .banner { width: 1000px; margin: 0 auto; padding: 15px; } .title { width: 250px; margin: 0 auto; margin-top: 32px; text-align:center; } .banner h1 { font-size: 20px; font-weight: lighter; color: #FFF; margin: 15px 10px 0; padding: 5px 40px; text-align: center; line-height: 28px; } .greenbutton { background: url(../images/greenbutton.jpg) repeat-x; height: 54px; width: 224px; line-height: 54px; display: inline-block; text-align: center; border-radius: 3px; border: solid 1px #1D692D; color: #fff; font-size: 22px; letter-spacing: 1px; text-shadow: 1px 1px 1px #000; } .greenbutton:hover { background: url(../images/greenbutton.jpg) repeat-x bottom; } .redbutton { background: url(../images/redbutton.jpg) repeat-x; height: 54px; width: 224px; line-height: 54px; display: inline-block; text-align: center; border-radius: 3px; border: solid 1px #7D180A; color: #fff; font-size: 22px; letter-spacing: 1px; text-shadow: 1px 1px 1px #000; } .redbutton:hover { background: url(../images/redbutton.jpg) repeat-x bottom; } .banner-button { width: 460px; margin: 0 auto; margin-top: 30px; } .banner-link { width: 250px; margin: 0 auto; margin-top: 15px; padding: 5px; text-align: center; } .banner-link a { color: #fff; font-weight: 700; letter-spacing: 1px; } .banner-link a:hover { color: #000; } .mid-wrapper { width: 100%; border-top: 1px solid #2A8729; padding-top: 15px; } .tabs { width: 1000px; margin: 0 auto; padding: 3px; margin-top: 5px; margin-bottom: 25px; } .tab-bar li { width: 230px; padding: 3px; text-align: center; float: left; margin-right: 5px; margin-left: 6px; } .tab-bar li a { display: inline-block; } .tab-bar li a:hover > p, .tab-bar li a:hover > h2 { color: #1D692D; } .tab-bar li a p, .tab-bar li a h2 { color: #404028; margin-top: 8px; line-height: 1.2; } .tab-bar li a h2 { font-weight: 700; text-transform: uppercase; } .withborder { background: url(../images/separator.jpg) no-repeat; } .gabout, .gcommunity, .gdownloads, .gdocuments { height: 80px; width: 230px; padding-top: 118px; } .gabout { background: url(../images/about.jpg) no-repeat 50% 0; } .gcommunity { background: url(../images/community.jpg) no-repeat 50% 0; } .gdocuments { background: url(../images/documents.jpg) no-repeat 50% 0; } .gdownloads { background: url(../images/downloads.jpg) no-repeat 50% 0; } .tabs li.active a, .gabout:hover, .gcommunity:hover, .gdocuments:hover, .gdownloads:hover { background-position: 50% -220px; } .tabs div { display:none; } .tabs div.active { display: block; } .tab-box { color: #3F3F27; border: 1px solid #DDDDD5; padding: 25px 35px; position: relative; margin-top: 20px; border-radius: 3px; } .tab-box h1 { font-size: 28px; color: #2A8729; } .tab-box p { margin: 0 0 9px; } .tab-box ul { padding-left: 40px; } .tab-box li { list-style: disc; margin: 0 0 9px; } .tab-box a, .latest a { color: #3F3F27; text-decoration: underline; } .tab-box a:hover, .latest a:hover { color: #1D692D; } .arrow { background: url(../images/arrow.png) no-repeat; position: absolute; left: 115px; top: -7px; height: 10px; width: 20px; } pre { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 14px; color: #333333; display: block; padding: 8.5px; margin: 0 0 9px; font-size: 14px; line-height: 18px; word-break: break-all; word-wrap: break-word; white-space: pre; white-space: pre-wrap; background-color: #EEFFCC; border-top: 1px solid #A9CC99; border-bottom: 1px solid #A9CC99; } .user-wrapper { background: url(../images/banner-bg.jpg) repeat; height: 110px; } .users { width: 1000px; padding: 20px 5px; margin: 0 auto; color: #fff; } .users h3 { font-size: 12px; margin-left: 5px; padding-top: 15px; } .users h2 { font-size: 26px; margin-left: 5px; } .users .left-details { width: 120px; float: left; height: 66px; background: url(../images/footer-arrow.png) no-repeat top right; padding-right: 15px; text-align: right; } .users .company-logos { float: left; width: 820px; height: 70px; margin-left: 20px; } .users .company-logos a img { float: left; border: solid 1px #004000; margin: 0 6px; } .users .company-logos a:hover img { border: solid 1px #000; } .footer { background-color: #F8F8F3; display: block; height: 70px; } .footer .footer-wp { margin: 0 auto; padding: 15px 5px; width: 930px; background: url(../images/footer-logo.jpg) no-repeat 0 50%; padding-left: 70px; } .footer-wp a { color: #3F3F27; text-decoration: underline; } .footer-wp a:hover { color: #1D692D; } gunicorn-17.5/docs/site/deploy.html000066400000000000000000000005271216475616600173720ustar00rootroot00000000000000 Green Unicorn - Deployment

Redirecting to here

gunicorn-17.5/docs/site/deployment.html000066400000000000000000000005261216475616600202550ustar00rootroot00000000000000 Green Unicorn - Deployment

Redirecting to here

gunicorn-17.5/docs/site/design.html000066400000000000000000000004511216475616600173430ustar00rootroot00000000000000 Green Unicorn - Design

Redirecting to here

gunicorn-17.5/docs/site/faq.html000066400000000000000000000004461216475616600166450ustar00rootroot00000000000000 Green Unicorn - FAQ

Redirecting to here

gunicorn-17.5/docs/site/images/000077500000000000000000000000001216475616600164515ustar00rootroot00000000000000gunicorn-17.5/docs/site/images/about.jpg000066400000000000000000000422171216475616600202730ustar00rootroot00000000000000JFIFHHCCf 7!" #1$2Ab%'BQRa3CG !1"AQ2aq#BRb3rCS$4s%Tc ?a@@@@@@@@@@[OX/ڭJ#{V*ՃsJ93(6i%ս#3q6*o9#A῟?fS"#"1?(22nYynEvilX+8 C(mݝNݬ띯UaO<:rGPY@{vruяTEsŤȐyFnqz7ޭ~;TM>^Mݾʲed?ȗ     hb)YdWB֭,B 77fnv[S3LSm._'&K_.;Gy qSyJzfp;[[j z=b3[JqN<-Y~_=ߒl9Mmy4m|̸MqBQG2יV^˞u ?=ԲZ&g'xFVQC)0^%Zm0@Ӆݯ)֖2o-hcitč/|_zݍ[^%JrqByD*+bN "<%\du Ƣ+}L@     bq:w7j51)2IU`1̒f7wig(.RxHY*"o~W~fTǟ|Nr - V)U'v°Wƪ̺‡͇-3D]6ߓ&_bޜ}q!:iJqR Z31{&B<\$g9oo/}HD ߻ ۖh^?ekJ@Nb8e]7x좛O)׊x`7!f׷N _cunNivl T9?Ӳ`̓N=B7t)V VkdegH~E:< ,`n<~MV7fEGS/Ģ?R?    ܅^ov7o&|Gk,~ݷUmD6w*jxҤZ0Ǐ[ޢ^>/1r2K$X-4sZ0w~ cǶ{߼|*  q ^Ɂ{o:U(Y@o;;>jī9IWU_Sٷ8yvk?u.3#O16(dWLgf-k"?3J.wIE5$5#Q@@@@+e/\+`Uu;dU!@eW4Qۭ:Jl:YB~ɢTVtL#V_n9)_GD1fRR)=QȤB;O/ٔ.OMp>H'b) ]Dowy~ ~,P"KچLj1*z#dv]ړ5RəRi]9k濽xlAl#24:]9ūK~ДͭfTC[(Noe$9h8NT׳Z_1e/%)y≹\@@@@V~VΖɱ1qXjjwr5c&H^6޷O&!Aμ4*뿻x^#_UpK;`-o^nr>>@ "c1y.4Jix;{rȕriv?!T9eq Th Ofbg$3wç:N4v}/9 {' VR"U&,h3ԪȤIumsjto4_hpDɓi{txbXxqfYز,ci?cGusF4:V H坱36[7RN˫o+-8me1~yi}U)e 7?!x(/"q&٢1򹏂lu u yHc3n/bЩKyc_}P"ۈ^/,!=wqcNKcK9Z; "hŁ$[ڋ5%E㫍EG?wdA@@@@ztW( j :݈ $p$aniCȄjy rmsmLB` Xp bc=*\{#$kŌ*RUw]oȌMӜ;NIBoxOe[svW-l  %j 07v6bYJR*r|Ϳo&Y+R` &;Gj` x Hpw?niCDTa(?E+6`     XYK- mˎp.yx-_qfee^RxLk*޲RLER>珿( ,N?kT-+pW= \3 Q6cxHo.g^ZTJj =BY-1 8`L7Zw=@<iw/Y*Ē-,|[B +oU{ 1zI/s(IٚKZx}2;+UCҮ\x`-ѸI | Co_5'ٶ*LES"VةܹHNLҫsY,yG?!Yx̱={B;S[R7Š$?}L@@@rRѥ){6.a6ljyRɫyXݙ7"@~*jJY~-5v =Z:Z#a/R(rN?+h6d ׈%,X =VE)FVi{=`gU<?dB45 H^%RZ6qNM;<]S*O81nwZGW#&{BIɋ/(B?{Rϴk)f?[JizkAcL R g)wnʔHJߣ[cЮ И {.֡H>Pճ@.IȄF8E'&K@`G`[0H 1e7o5kƐ-#4Zm&KU%7/ĥ/w]@@@@~6+VЇb A,$8Ivv` N6׻>ӧKGJ!1&623\ ,].wԤuQoP"U?s*;2gҋggo+s  q~Wf ʄiVG*!ztK9Ո"q%ڛR_F/iU~˞p+_e5~kɩbƼ%>ӕ"ڶnݣ.!hiF1 HWM     ?ux['diYKݯa2޵3ǻrAI@qSMT3c%2]{O̦[,}l+m?!_kx4}> v]xbl ږܭ`!9Up;'R/K.44 PD,}B@? Dy|#OJאMը]n.ތsѷ6+Yҕ{˚!կViGTqwM|E+W>|9HuB̅'-2#lщ*yF`GMhͩ4Ml[UѬ迏i,xIz+l\ީcʵ)|>Ygdd?æ:_~:2y?pnG( y;N?6xG}V];ѴQSo& ^5OGUvHG~s x&]~?l'SncVJG`|Θ h. 6@1Z$,veB^֥(J[,Ml׽,^jTWvVN|~Vػz';۱/KB|0 (ŵJb՟6D ȕ6}k[[fxVQH*M{ٞPu:]k{G[Ws)(v]즱MZn0/в=M{bo71v+F *3J9.R==vF\ @@@@@C&4LE#d6}7n7@Âl9r@/\Dm(6mYWٶ]AOrEΥIFEfS7Q[$}0Ӕ)SjӍ*T*'9(B)51NRi(KsF=G~֟zy9K|+8Xi0эNj'4'-%KKUkvcw]wg4b}R{ƭLAMyND2.IUq4Qa4)J|^ ԯ:w^ugvSڝ(JqW4wsjWr{zӥ4(QiӇ#bbhҳ4-;cybj9s-{ XIVcZdf~,ZŭXֶZ޴}*NH'/F{Z(U_ cg~Qmn)=jeW^ Vke x(C# K5rҺ2IJR~˫K?}Ÿ,iS꿀{[ٵ?#l$8kH]., ső(簆hP _&uN*:'M-mm֗Q涫%B{ƥ9'q}3SִmSN]SU)m(/xT5(U.'F[b@@@@Tu={/LM2ZM/u RXmL5'hu,M_k؊Yx9O:zyd^(@^1x"5R2ye؁W\V^W~юo;J{*k.d闋kF TuN5Y VB);V^`GNh2ӧ6@@?4-bs.Jm~6^(J]w1(|fn5,a}:sQ-Jj4{[jV>J#BXbJ;FօwU*PR}mN\(F_VR㍎Ův̏ӱ/6a>8 vpE)D~OMn-mΨ=џ*    >g1Xz<5MO ^05N>"9w)2t0yoPק|Bl'7 ЉwRj{ҸKV4ڍivpZ?]&V7=::ƛ 5iЭӷus(E;O=ކy+ ^4h\}VU /%rQx FqT/Tً'Wögq>ʥcuRS&2%N2RڔdT駺ktú?v;Dֆl00;DXڱR_,6g#y6\ƤY{07NSMNݲ7~kU*n2cZ{7ܼ#.!+Um:խx]ГW9?eB@@@cy}g:Fr|QZݠ9Prm4{kL01 x±,>+2:ve'S=:em}jpx*VQW4Yu6Yk·tsn9)SufǦj*s-7Hɇ@KX{fqoخ)ηƟ˄{Jkj ǪʭEGQRe9o>Q<=>FTW1+^)ʚ3C ,?gg4.QJ! M=K{c#z;nW4ݡ*03d1wBcmCQҭ׫֩(ԄTJRtmqZku҉i[}.W] Z⻚g T9v3%,&Oo.qXW"{_[vw_&,ӀX)bu*7 9L\9L}oD֣avԨQTM֍;hɬJҋXi6W^Hi|ccXawgTQvq 6'qQ(8I.OchضWɸg5r'Aşյu0PR<[ !D(iVkyN϶B8C5nNEw{}Fn-mk[TOQ8mT>IղѭѧNhA^f.c_64|XSwaV15,7i=#TZSZ4SHЩ4u٦G~zpq uInqLzfɯ/p\5VAuUOxg>ۉɸiRz>7nB    576.:yP%z _K˜0bzչ)S?@/[ʿjv7Q^j5aJ M~1ѭ{ZW| 8,Vi{^QU?Fu'}+'@n79r}D x}v#֖Ѿ+㰕cXa\fUH1JbGg𥖱T*k4h]:hu(8RnVJuju| Y鶕8Eoק6)ҕ*Zae#u]T}="b5岻uߧz:ZMoYh AS2D`gXp:5nVeN9(NRq]^_D|c}zVy>Ε+hEՔaysq4Y}"e֧ÜxS3ǹ,f,5h͛OF,/;S "DO)+zd]>歜XխI&8ef]מ3vz&R^T)n9G9P}8m{^xصK[il7q9$Fab`F1GbmVUeVGr춎3V1Niwwkүi^wqi8ʌ;YMϱy$\x#5`c1x5d5OZ'cWٲЄslv'$ZqL^go VO?}gV^YχJ pumq{kjJu޾,{cŷM@@@Y9A3>2 t Ã3wB6cԯӡ2!EV4jT#,niNkj쯭mg%Sini46.-jmqJT%.hjI4,vg5l&;`s]s9@([ xiw 1wٗ+Pkq^ִyk[֩B~JSp%p]{ ˫\YVVIR+/nx l<-'J_Wyq>m^#;CLqe Y, `&'ֶ B:u hN,^S9}}%uljo Z:M*םIsb%o®[yy03GK#?CWԳ nrpkeb[Kk٫Y[e-myJ̳('t+pOյK[:UTԶRI%Ț뗒.euu8c܆yb+/ $#ebcW~7pijJc9㮷j6!EHXҐ}ܳzK4צe*RXN3I2ݯEzVcs^MbJ%ҕ+ycƑ5b@@@ozvŒZxk#3t L^X1(J>XsQ%_O GkẤ0Oxïԍ3Z5AG¥-T/;%ZyK1}"hq v( }\)^B<.%asFJ*iRgPX( ky/~L4l1Yףm~`>.^Y*v#xʱ?[y}оOQ\mr,rɦ׉ҸSѭuK;GW[Ź{JJQR[w[X܈+/nۭ[! zL]1ـ8@ќa'3jQM$)%ǼFHqITdfM'$Iƥ$tӨ9)ڮAQrF;w6Ny{n*͊Jk+fjoh_XJ38\ɬJ/4LǕen𤸛h2T5uek9gT#]N8?+m|G;zlf*msID^*#y9@jȏ$OѴbK۪_2?^$X    |Zk0qڄ#kxEb$jƅ[uWhBhڬLڅ RUni7T_΃R>1IPBIN.2\ŵcsэŭ)QJ}% y5ބ:u"ԌjAp9>ǥ]xۅ}G>L zl5!< Vxrsk*RKZО65Vܒg~+zuwkO=W )Z\U)Ī0   W8cypkƸCfћ] v G3|dF1kkc bn1Zm{oV76{o.ZՇX5$,=/Okiڥ;ZEg ,JX>:g o'מ.MnAң}.Gι[z4ⶈ"Zx!O!1`*5k N94W.w:G>b(/VRPۥg̜_WѧRB]cM˗cͩG}F zTj߶cQ 2$`DQAsf p4&vvgeqn2MI6k 5M=O5 (JP\e(5(<8=Of韚(@נhUrT\@#Td};g.ꞽAZ8hTbB2%iAiW0qRgr0yiggU=žxJ-3K+z9EԦ泶|.o_+跦~0?$Onǐ3^~`-Zx 49OPt,q*loA}VƓJ|죔Vi.Z{˒QS|\gSp 'cZ] J|]ԂiTs˙rRaJ48GNWʍxUqFVPE W cf.zI-#>}j     ٸ_.G3 NjX#(8LȂA jڌjȁNf'T^EAΎ\X9s&HHpu?XbH,NtxUU=x|ޏk"lÚf[]kO6V棅Q>oZ,UҭWk*{{?VyBOiߺ~<nO)yr*%bDlrzy0ndP]ףM.nNQI< wpO])?5oN56ܯ{ErΧ*R)? +EC}n!ۼ=ɰI#_ =|ZFBUv%4\Iۼў<{Koo\sĘ_`5ܣB hP񄇤(:hPϟMZIY ^ '[hIENDB`gunicorn-17.5/docs/site/images/banner-bg.jpg000066400000000000000000000011431216475616600210050ustar00rootroot00000000000000JFIFHHCC &"#!$%B ,!123A"#Q4BTCRSab ?˹˸.s{S?VA 3_*"v78f{$0IDt8֤xDDpW]ժ+;G]v-D|[gunicorn-17.5/docs/site/images/community.jpg000066400000000000000000000363511216475616600212070ustar00rootroot00000000000000JFIFHHCCf 7 ! "#1A2%QaB$&34CRVC !1"AQ2aqBR#3SbrC$4cD ?a@@@@@@@@@@s<lke"r4,90c93wu,R2J`%zx0{PrIGb-yEr2X/(P)/Sal{:l/n^A7_&ሱXqwvxMшA̝wvBiu%p<Foܥj6/%Ҧ7+j؍b6'?^㱦g7(h^ 6W 68~9WN~,,k=5^ل+1dziSH@cm,HO8Zxֆz:2'g;sR{r&y?̳xYBkg3b$k8ݧg ̀)+!FK3ġ{z]3i`˯?_I:ֵZp\`YZbi 0!DHJQ].쮓Me<Ѯ:     <&ﺶ4q'3ƕMo\6XwՒ?O@}l_g(yGNNL$-7dRŔq䟲d/mc']#[9PV9`{BA12q[R+x=nɂ%s|qmWB/vlع`֭1lZd=67!sR!bJD)I)LRy;o;ݷLp k^sPȶOqC-,#cX^j8qϴ"<'*o1ד^+!Դo0Zכbo\"u/W d.JI)!\{нW=J5xԥ>>k$k-'-if16{,ԴC$$HĠ4&BR{S;@@@@t{a2yURZ&xwϷE\ IhjrGd;D+e.KH~Ê'f.>XV FZNI?bgkjp^'_y Pg!k "o7 #ga00#bعINOyuP@@J\Svt>B-fߍISgfD%VoY^ŷx˯E^\Μ%KdGlv=R0l`b `_f!];E< fNe( >s tv~]z%),?U @@@YΘk)D VUn Z+Fsiy26'}}Ƨ 9FU ]ywdVXkKFJ_k+R'S#ٹ&=Cf.43qG/ӄ%M)^ WY?͒gq-솳ѻ珱;8dffr^+*~2}_$GSzR{bKU20,YQEqɝ;y?"*~e_ەcfk}`,l*7(ZL;1Jefwd2_eS^a%Qx&x.?óvvvS"~  f ۓ/3i!|x8vbem̄s'^)̄ylb-FyW R'<~+, $   8HF9?F1n37W@b+ḯ [Jʹ,Fw91# B(ìZaj4c.eyJ_v3+/"@ ?GpVnF=_nf~Ps԰uo !Rhţ|8̥O!I)Nr~.;N*0WDB]Y-FTՋ]3ڰyY8xַJD7-)7VoǯOF[VI ɾKc!PS>:,wa26~34&y!wowArٯiyhecJ@]n 8 tVgco_H^ـ䃲TjQQn ] Ӱ7<ЃB|4=[W|?>W"+@@@@GXXv`zuu0C! ^.8vW?3\6W`ĝũ)~5g̨1_"@  g-۱u;F60V:5Iۧ{u;ݣ~%)GİߣKY u $B`gy;n~3i,HR=zWXk' B9kV%/Jџ`m%>֣y?La㍉N|")v.>øvnfnYFJ4W_5+rx{]YJ]  oc'or#,׏o)}J) S   W)eKO@a @q"ndiKakϚs;'8F}ÝḣA,aRR8˿Y+.|V@NEh9eolCcUqв /_t:tnq}so51'Z?6^ڝ~>/%U|?؃0@@ ["$!JRI1pgIF<'hŝq[zNҰvN_"ើjdO;]fҫ_ -[3>.ѱFog3SU{7q)-O<ڃ?).YuC"5@  >-_ TkE+UX`ēDa23;Yo]Ah8o3 C\[<$ R#TW0@XkkDIXXuz8Ongz͊| jC >9N%ː쿒W%f]IfD!%.0담ᐄ8ol^OH @@@@}qLSqϊ&cWOf53<)TLFq"ҝ%'Qt~.^E7#F 8g!&Qc ќ'n쨓HJ<>S3`B}F ӀsftdhR1r@+|-;f9#.0=aUMW$(A`%}6v!-c1v'Kq]Wʙu(WZNM^2o$uwNtc9^KxSd%r}'] =G,>S'gb]<ٛ4y}!#6Y#y;1     M],+(9REp`HyXǞ8揮B̦|3old5m'IijVv1k1wW4hscj1Jߩo'r: ,~>rUCz|i-FoB, {>yGj_|sxK< k>H5xnq_*RN_r<>>2Kc88ۇoTnCVdAX- t]"[GXT-S?q~ (_:VQw.K4/m1 ?]1Y;MT:2q{c{jZ@ɚ2jF)iƚ=f*IkEVQi'yKXzJE-+ 5nR߮UgYWc̳.w7RroETw&o Z恂^gԣ-^qFIl s絥R8ַN*)5!5 e=%.wPSn48K8jLjR0?sFk1jK~t;e0    As)gpBI1 9;F0Y)I٢R9>j%W&NT2t.[dʑ@za^>1(:,D!JՌN[XT=q=9╼eRrMmWUӴ+*uJ֖3:ړ-:pYZN2Msw/zkśu;RZvas>)K)CVwIץ#Q[4s[{pP`gzukx[u8UszdյUؽ*xg֎}TԗZ_ub1lY+y-f2\Y<ȇzOjɧ'yL,);ּZz^Jf:g*&9)?{f;ky^VJl2@-p~ wJ[|@G ͆jيi0&emKJifmSNҩe_}'lu+ \=\.?ͯ-' m]Svδ=foZt3b:1Zj35::} z<´.3r9۹6۶'㝶-sbLN:Vs^1F9?'uIڬfz& J#,),<¤^t7ouN JI'2SU@@@@J=Hs雖߸eK[^)[;}\);v*솤._h@a5}VN-IwU9lsNX55u]FF{RYmJތ>Uj+W5IӅIGUOQޥMv0%M/GJ@tlhC_7hEҵZwo,S;E!s_kn|ڶ}7N=9{J[(qqfź/8Pa&YҖ;Vj1usU(SyXC˄@C>2g6o-^}դs:yM;\>bIؼOJ*9i-mN>{py-E] [hZksU-.r}عn݌^}[j太A[ȬkVa*Աb"͟ q{n—7$L)'(jH F7vMs'TjCSɿQҔ^c%^Oēb@@@@eKZ<{f8Bݍf3kxW+NQQԣ K8wr.&yCR{ӗ,۵m()Jb#oq|]>sYrS8󔬦K?ȩB?ɼb5svZܔjdiQxJun%PywbYm$ԒkF>@@@^k8y`ʎ7\!vr^ĉq&vw{UkdA_Kѵ ri:uɯrkQ|ҢJ-|$SzBe%Rģqy}_P!tR57\qP@ &-ӯ-s͌4"@u1ݳ4 9s^p2:u*N^:6U`&5oÜCyMUuRTSkN EgCq,nBD9B.2u3(R'G;?oUFMlo,/m;ZoXGo{jRtF<$lnmSoN(':VF4 n5||<5-J֧NYMjˈ+ߦk1 h_Bj:[ msϹ^ T,m,K]j|=f-`b]_]A9 )5as>F6?vKe ֱӷjzFeF\"I1<$Qģ )A5:rtx5%u] jQT(IZqk 4ή@@@@UtoNڛl.Wݩ.vf`0`'<#®?0D%~-3.4t{Eԝ_&FǞU$g/ ӵTLITF q᜼amjS;xCsvʛ>>v4 9?^ _pxƾ+Zo#5/pNQ.lԓTa&ԋޕXx(6pОxNݖ'״_Wr\CO\n>9|0L~DްJ->K]h5䛞wS71 }-G~oМYNӚr~Nrd=zԄ@@@ڑZAf 4ߣ984?wPjk9W*QP݋h-uc[h"EYz'fhQ+J[|WͿsRz-*R*W;m;ikSH5r"V _`O}o/wQOO|OO&|(:\ul9d%\YcNmp8EXiwo;y)RBpR_M4ג;B-]#N J2n-A)BX8I8/t2H7o]^CŞ.nbw v kvW'Yv;e!E^O=ae׹T9ӚTe˞*Ϩ^^9/N燳>֕,7<'/="ckF5EY/[h.x?^:?K{p5K2")?~9^:4iЀ 6g^<*0K"# xBYC$bBġ]cFс~)MF558x}zi9=G+ e64JS#i<>?j^ITVwm {b@@@@kWp~7Ks{'Kso;;c+F DPSe[kJXTTsק{Iv=~ZM8jԽ:òG>"`_#Xܻ-ܬ2*;}ܖCW )yD0[W쩹OJVRYF1NPe9⽼nݾqj4.ssJޯ+TgN\V{D]eli޿lǶQn[%x0)e,VKɜ/T-IVUխq-)ݓxj>x%'W35H3GNqswҥ; 1$ ϴ9eb~x<󜷸 ҟ)1T9CI hR2w-S hA;}ZTnJ [Jy^ܜjTrKfmiq=ZUBYNƏ2ڬ v</?Oyn&ۿNwܯxYJ`Ɓqږ@Oᥭ29CQJKCiڄ[넹`[Ђ4(y ]!]ϯڴmNG-PXYnOdbs[YLQhA你l@S1qiyHq ԒZxj5t->N}[녴#˷/*Ԟ8 2GڨjwK1Tn֗Y:'?\|?zS2tmQ8`D(u7,\_Fad ?Ipu@)J.2VUnMmyq:{ƮSM$vGe?FIp򶾯Z=}p$Y.^A@@@@y^ l>6xf֝Jjg^W5aNx^%|ݠO&&Bp uiBkXԆKlӊ;rF>9褵ץ]Zԥk&p%nk|zZZJT6kWg1Ih%Zޝl= i+9;ݙ23şYNZ;;km483k:ݍ=NnR$7ˉӐ1^Fqٶ;rIrqc;m+mzruZ;9rROsq^q= U)r>FoEKԭ{JSSV<@{0s<ȺGw} nB#.ZjJ&G$v^iu5K?ĺNI9?t#?rfOF+k:k}wF5$g4Uy4՗pλ<\#Ym_^n;HZbsRӵFVw_jr$MQ%$*0mE? VPX~+>>h*4iя-+z4ӏͧJ XE"uWd@@@@/bxC[^v{-wX2b W?c'emq⦢jAeR*FQ{Ic|K#5#:U#¤\e%(5E%Ogj şMٟ)YpC5gg;XG֩rXl\QF({cל4IhZkdUŅMڝF# Q8^ k6J ΙUNOߵVYW;X/:xЀ  5K⣘+Ɠ/qT<{'sd}ͱvsg`L`hBq>IEkA5;M1Iu S5\_=e(_B).zW{IմTgyy$iMR^fzOR0[DU|7[1M[{JQbmnXAB+7΄o/'S   vi3Lgggn(|KsÃvv3|_ǴJ5ӿK+Kfz%%̭iΝ]o:X|}~a0 xE{ab)r]s.^mŷeiJ-T,)ӧB+F)/?=s*@@@@Yc6 ]6f̀|(JDH@8f3ApBQRN2YO}UHjutnQ 玩qxsgV?-9cPTc%:IkI;ljRLW?Z*7+*^}EE\&FOz'ݗXO~}w&g7Ɠ?;ÇZ36sifQ c}𞯢9TGlMu(r\wKmD99c=q N-yE:%RG|z7i,c]^)֫Ԩ̞ [7l:n s` Q I0#)Iߣ31sQb&I%o"ztUi҄TaN\9=c)IIe;ӷ7sϟiv"f'2"+3ī.Kc^Ib<wu*w:=ģh}ٽMPYS[jZ^"V^Ρr*{;J,؋8Wul\N>(kb͟&õE,ndc1 bB6V4-(ҡF;kj -i<{$rmMM-eegPӭ[CJya:&9ɹnS99;"@@@@@esMɟGFlL<ƟZpƛ}U>mRS}׶|mI8)&O~i<#ȹ6YCo۰HaBA^` pWts{VIe=K-BNutoQʾ?VolonӠn]{,McѧkRZll&)6R.sܲ'1&8{ɜoJŲDL^$ОK qvќZER1BP+2KXoU.ju߽%w^lg'L=ӗx! .U%,yw'ԱWgk,Թ[=?X"\#|oo.]GHєa<*Z4ҡ$o5Ս4)ǔx?Aa;A%-GU/֝%BʜIF1 yߛ zE8R>w(ŗf"b#,Y0N#x~L;JǨX[YKuoT*),,8lxs[[KպyMJs{NMų\|Qgš{f&[`firodG q']nOogo=¨@                         ?gunicorn-17.5/docs/site/images/documents.jpg000066400000000000000000000422361216475616600211630ustar00rootroot00000000000000JFIFHHCCf J! "1#A$Q234V%57Rabquv&8FSP!1"AQ2aqBR#Sbr3Tc$%56fstu&'4 ?a@@@@@@@@@@Lp@W)v;hRmPyyA1RQ^0Y?>֩.Lc;|UnEB,&z,K?زnYy}D܇b\0c!CMaJ1ndn :UB~G$혺3699~NЌAWZ_T9_0P"(se 2ݤUH@@@@@olNSٛ^KFFK9t`QNfd14Nq̟.$I /'L,]6i/>L(eБG?7FкKc=kB p'Viwا#Vk{2u´F.1]#}wMA6ˏPBV%( f)f9{/d[y>e w<QqS =ѥ(qNzu/{fn#i%qZ]jK{r"ܑm7/٧]_/%=npHJ[6,S&+fbnurԎH9F.AbF3\aN?6qg!ʾ YY K/3O jƴByc.SBFW̑I+=%EOOl%0[$TZ,fgY9Rla24|mXyH-ZIkr$PfrQv!{IazU |[?'Efb$ i (P iBpQvvweqtJ}@@@@fl5ӫJ3[.hҩk>FcSX"c%?JA]^Ʉ4#r][DRu!.|&O1yp&#Y&>R/:Jr䇟cɍfIԭ+u?F~;//>4Zfܻ`),Yh=,e1YH)&2'yIZ7&> M?䣓]yˤ.T#ynIu=&PО%M/kcrIqƼU`(7N1%`zNl1IdF02HB&\lQ:-Q/w<:|׃d(Tc- >f˸f [2gR98qZ5$yO)@@@@Rsv2j]R۴On1E>e g11CPb%Rn﨔Ȼ:0I%O+ flYȦCQ<㵪tkʩ}KUY@@UY̦%8zӬe(Ls@qN`4p,d9/4d%(5>4@mc6n]ŻrL'Bw~i].0`JTÓQm%:u~{9NK_l$$    0&^,-wIː٪yU54yR{VMv弲xeٮk!2Yܩ \Gyd[a*k$[3'҄(LZqvK|H}:^Ok/bd>[&ͯzV qVW\JQro ɑՙJ'(/Q_I qy=4SHѩ?G6)Ͼbws<2r#<<yNp){RuFoW@@@@,hĀO)xn(LR 8̒l=rv+ÿȚhIJw-9~9Sža\.K3y0b #yK"H/j/sg^юo ݟoruP+ϬN?AˇM_~yV;O? ?Ot:))c.ߐ?#2_V"QԠ  7`*&KxUd% Ffj~c$eR;ǧ{l4{[SS)~%)@@@$"QSn$% &K:Y@!,eis21̳\_O#OZ/TѲK6KeL1 :];/w2Nײ|ֱˎ9!͟wO,FN-]]wfws*~}9!&h6/|TW`DV(1g߯EBbk&O>< %6.3}"|‘ф۬ٺ7ٞde kgNiByl(+xɟgovTeKD@@y~3Ƙ<>JٝòwDċ4_aE]yoROٱ U @@@@}T7YY\Uqܩ [MJ.Řœ:lC*ыZֆdE^<y~'OoS^٣j+-[jٮI0!EHIP^.Mcg[4 @)U"8eni ynϕ#|;4XАܟ7IrNALRXOWnoZ͗H6u?螵/; lSoR.KF`zt$Œ p4&v aC>8\F3#6|yU`rޔ|BxN4x%J̰ia/"y Uvʼuϴ,b@ aNAѼOBpx,@D&ʵ)\SV;eo |Mq;קʎan9{^DaCnBpln*6w_޲S旵Qet]"%uldT@@@@@0V9inX8"7%YLZ2tĎIÞ.9NJkZ5dS9gV4K;/'÷EWgFx]l;;<맳pjcMF|53U|<=R`  kG j- 0W",bERNQyNNы;2]&qiZ$xr;Uiָ'`L^d\^`#q|AB~Kg]퍷/r~?D_    ڕˮvM4>Q굼I=bxeV%uNnU!RK >! ~>wczAy Hxv nGrN1PIQ79Vh>asV3:y^IIyyKuC0& U*72VCV불RIb/FD$ߧW2M$}݂Y2mRrl7+,u\qV`g̛kKF/B._$gjC˺Ojtf8'?ra]uK:?ϕ2yUhDz?}o˫ܨ3 xkz_qNq|Γn%+3q &|X."&,]ICoUn<|%Mo|v1nֱRd5{A%sqx% 7E6Tq̘sLX<&_2AǼߐ#U |wI>;7^e/f2R:$=9҂w?_cGXYٳk;¹Xu%^~[UXey轩#3̒G-_EH7[RKU :'0#?"ލ{p yNN^rí%𦾎m{7翹{?$_k8JDGfyj݂?Qj-}qV E3W3UՙC4[?s6Fuz~JhS=K }KN@                                                                                                           v=[+.V2vqT`knx rU{ IԄ6oEo'IaNBH7<0r돥Ћ?<^Ǔ,;#bysgc{qN^J|R~M;c%:LMORM\ѷ^Nqu v)>'~뽰m)-^6WJ#hq;1k\׾ڽajn<խ\/ۭE̥MץԪ]I}:9U~Ǭ\iݞh9{ys^8}ϪilF66Rģ[e_jK/;vWG|BslxLQ{`G+ 9#,|h2ּiX-f&kDF~~C`ükas+g0E{=M4*X4jv6.!iiOi\Ow9ovYI %ɼ7[,uMWM+KZX>iӧ՞%*q+xO/xiKz% yqFG!Xk;+mc MX\s[{p[z}];~3慺}!V/x֏^5]BUmxvK>R1Z;58xEUo_g6ləg#dm岷bBn2Ӝjծ*Jz+՛֜Tܤj.+\Ty^֨V9?|)*@  iYE8W,k06}F;M8.NNVS{w:|8Oz`36G Z-6\l ީi;dqi tJ390gweGvٔeo4WP~!%O*Ï4kx[IwSDB@A/ovL20wPV:2^w_ԝպlB'ݭYwkܼm.i REMa՞|GzKjt-_r=˛dž2tmïQZI  >^(r܉9R c46-Jw7>iRyɵJHT6 ʼncGA;9iwr)JO_N~n\yJR\'.TwU%+9UN~.vS(lЩo7Nr{_Q'K%Nfj ,W'ItwGfvPM4W(     j \K|8s;kjMWn6Kr֘8=r%5+Pw0N2 \]+E.˖>ѧYR>SK_ΥMˇOR7 +9'(^f .\rBx 6Ǿ TmE'8'i5-/)uO芟 J*]|5w_5_ ϴ,&YK%duk c9b;BvomuÈjׯRSfSQ6[%Eͥ;{+:\]^;zyJP6YyoI'm߅ׅrK1ꁵk9b]i_27ؑgi/G$(uqBXklۤ>~&?E<-ET!-iRvj<υlNJKO%G f28l>&*9IQ ӹVL388Pksx*PM)JQ\gǸÄ*]]U,;9Aԣ^4:2 PaD<4sCیb5"*+Y-Zb^Y޿s!\rL5*d([w%Y6=3j;}f0ORHQ۬%BAzBq;â(ũE>jn;NO-5IG9 ũFqMI|J##ͺi*n̉x+w+{þn PmqS՚Ȍ2Z-'P{IR#GoO]Ǟ֋}k\gbJiq5 VԨJʂ~yNjܸv4%.Ƿ\(Ӕj$@|UNJw+gڟZ!`+,-lf3=1uƶBlVZqueԪǶNJ_)6Z Kj*5RQ^iRN)%fO5zFæ/Krzǔc5A|>pQTD!e(Xk ---xEe *=^4w>μ{J]*km7Υ XzBV:nӒRTW2]ևeF{%ړ#=BSykY}nqŚV=AVhqfWH[?m.(Fƍz߬u%+%ZҶkwum7KmkЌƕYSNXK-G/ /r"__;{\ֶOLhk+]{.(yeduνxhf5(רѡqokF*:U9\7Jpxo5&mC-,Vxh<9US{8j-(+Y43oro e66f )w\p=T+&J}'E\+t=R˞~2]|oߓ }-G~M(ԓyrvs:Sܽ쟋f6    >er0H9"4_.<2?k´bЩ :!I]'tb23LWtVNnmNr״xaiSvR^c/:\@M 7 8 ]#/^F] :qhlۆ2fǀ6p#iE^f-C[~H]ZԌ*5% {.;ע*>p<3N \Nq]ۊrg%Imk%NAN,yڸ<U9&='K!8Vǧ:~3j}gVv/ԫtnHWUiԧeNXmm .QJPNS:.WpޛrZ3jѧ.Mi.Zt8P89TjKBn8Xm$if17h֋mZкN8MMTB6sRm+~{KIK0}oBacK?3X% iǴhV\_UT* rW|*]6>i=L Y_zBẖ:S 9\S]Ӣ֬5-_i)?0d/?s[5 Y :fYɛ7h] |@QÞ0$%{]?=<8M.Bx4(~Sd1} no~hF?8zě8{+l=ES:ҷm8{=<^Vt0    e>- Â1ʇwoFJ '$b;hMP( zMZ%^ZOΔx|+{i.ׇM&V/wyJ4ĹA簀􎝲Ŧ*K1bdQmy(M&l/9L'#iն\n`#LyVd]Nzәe[ 4?cv>TJW6E%ҝ5-r~ivzsCR z.qUjTS>O9|Ob J΋=d]c5Djyl /"PR Z&,[zպTBv)NCt~&mZƺsk^|RH8ak8}ej<DB̖ HȤ7tP6s,JyԳ&d[{7>?cX-?>S Cxim/zȫ;Ϳ`,.L5o^T,5fcO^ Bq1!81c[蔤/Rs^\J/|Q$=GkvJV={XKcl7     G|Ixhe*@7q^#D~!s^V kN0dl67~8tN:]|ֱ5J^2tNmQ*.1\Ҧ(\iSI.qYeVҝ8/&h^z|G꼃zorzL^>Wy6+l0EW_hj;J^EE\IQXFONQFyw9;ܸXѢLޡjYgXx@go;! jJj{7ԡYh9JO,O!iԩ^WlrS=YciWԇKW4P MJѡV۶:i ,ڲrɠ W̦1&p'hŝMr!Nrj1S'QmnөZ)Q:jIB:qNrxa)JOeo -c6s3.N&0yǾd~YO] cSIxdD2>-/_N[,mvlO)vےU#8?N*evj>]Bu۟7XU*2jz%jMg-cZhEruBd73d1vm@6Ofw+jAM Z4mQomoZ!O|Km)K-mrr=+eegZQ[CFyaV&9ɹԛ)JNO?+     ƙ.5Ʒ{QXo_>a^`_rtEsbS|$z.7)&[$Q[>Ϳk 8K;wbK=$ rM *`%3ܼ;lÃ3ڿ VN"{FCyz)Ne4ڞ/GrV99ymϣn*t\qKԦ*tSm9ͻپQ;;(U<ۧA38E\bFwpF=$:eCWvՂ^NtEzKhW~l''AwB]YP^NT&{cOkŷ8eXUi4'l?m̄Z\"=8]$.=e}zcԗa1 pN%8i2z4<HGm }oUcmaWX zY֓Ivo"YOFM9'uwҥNΓg5Rxͩ3 25MFǭ:})~cDŽ+ xM!/V'xf$Pzֆ-c{YRR|vTK;n}=sv$bdJ:^;Q[[O ;YT}Tʭ̗vJ}wHkF5õ[BŚLvbn߱A­(Dz{YfN0ed/r N@                         ?gunicorn-17.5/docs/site/images/downloads.jpg000066400000000000000000000371321216475616600211530ustar00rootroot00000000000000JFIFHHCCf H ! "#1$AB2346CVbgq&QWacuvL!1AQ"aq2BRb#r3CST4$Ucs ?aH#.I1c[u [ /(}H ͳߊO}|ps2%|%_aefBϏ(\7 >^b6^N(uymer4df&b+>5w]"*V\uwT puI58\{ߴI"<23Jx2 E2ǛJ/܉8ٙ2%rBjKxך{@|gC"}!BҞ*SeGʜu (BK?߂/!M][-w;gHvrm>Yq+#mNTͩ-Ñ?۱-$Y(Y y\ oVkKcmZԜdq8U=\x劕7L~ė[ZF,}~wKs~Wo.Fn2TH7qv=S[Y*qLg'4?FdFR<%%^pge/Yg? B>? Iu|$zxm*)ruuhukr$ 'GduAV$̲n_MzR!¼UmnL= XpM)n ^u-pmwS_DrI$ Ou|>j//>]$߃e56ͭ@IsZD҈!d 3#Ez H8ɰ&ԡy ^]|v\7G$i~DCQX1a fh5jK<;kK2@ ;("L)C(z4.֕|<r2 \[s)XE,S/ ؽKK\/2mIu 1a;Ύ|O4Q V¾uA{7/w&]۹[|4t£'ǘJkbm(Rym)LA%r O1jS̯Ԃ*^+C"|b76C#4JBM\K,JqIB{Ԕ3ƅSUrVZS%Ǣ/51%P qoI$?n?݆t(eF7;_UO*x'{!Ru9e:j["SjrI\)Ff-cpҺ,א~2JDo,d @vpv8 [t]]dI2_'](Bڗ׶~qTcʀd̎fORwtM OiXWjLS$9 ۆr*S$4~bᶛeD}&'Hꇵw?*dܕ_ؠ@)?a4Y1;ROuLg)9-;¸2R~1?6ݾГc_s)ǓKmk2m~[ bsOnTٟ ; $59ifki&4(K~\w%Ȫ]SLj}lIY?ʤ$'y}Xy i!)%^LU==°~xױRݟs јW''m)y{{T|YI8=zmOfNH)w9Q4"35yjH˱]R%}cDmJEIcuYG}|IזTqRDf/VHGT<T5Yw/)ST˧8ŕ@_` |nȲsjs\ Cþ>B7Y٦sSo۷/K&C4~e_;$uQEDVQfؒWVْk!-e5’~qjJZ\ξ&Rjq~ urDE4H>FX[hcR_R_.^ևחb -̰yu 䧚}Q%>GiYl;8ME'sosֿY+mں)qMjũFͿ_K 2jcOKPTڷW҆򥿧^Z͹%*'%2ZӨSn6!֓BдhZD*#%$Ȍ@Z MXÁ~]sշ0(εt#uX6fJn,HN5Ĵn4ڒՒ/,j_V?JKK˯eTUQC ".e[y$<. "-m[6{Șel9!d5aMqMkȔh춟aFMɌ9ؤ.E=O-a5,r#VKIplG+ZtTNʝamiJ\.r0KrQ!ŝt+SmMl[)Jߗ6]CVl24V2J\&\\vƧRF-)k!\s{.+M.ERc&1OA(6-^S& ή-TXɥScm\u[jp85Obε[ ZL1>J[j)8# &{O~Gy<--Ȼlokڼ7{x|<7J[NC0 lq-0P2fLGM} qodOQ{_ lױs&ϗu7,wEqP#4l]_AeZIe GFQDDOBǪ$r&<+JZۋ3>$b)9rI$%6Yjc*HYDSI#SM?:]ㄻBdzmq6u<E鬶Vu\uxQK7M Iv&g\nLMTq>_;Ex\6kxŖK`cIrDȋ-9=;]kY> -\؆tZ;VO=erฎPd]B1;&6|KVEm mDK[?Ċmx)`V06u^,Ϋ!G>Ģ"FIUe=H.8me^moښ^8$wKM~/êWSL>} j[dYK󿨟Qճme5%M+\殱=>3+3% B\4YɭL{rF3?XYޓ;z93[5mv5Sorĉ?陗QtϖMsc>vZXǿBufiQ<~'jGxٹnƩ겏/,([*3o䙝bRMjv?%}P^LE}-|&ێ;"ѤԖ^ECL^vﳗK:d^]_[+gGYLOJj/T$,LʗVک~[5n}{ۘ$,ƻ)&mYEYT-?mZzߪZiZͅ78mɀlIHmWOQ*TfDztOHcռF::yGǦ=ݛqJ]);Uc=KeKEQ*+UoIJO3j&h14%Ķ:mKbGڲ=12N4$3'!b|i1Qq3ʦo'9U5/5]|OxFJH-W7&KUˊ2]2g\liq arYkX={Gj]^nF meq쭶+Q)M7䙯3] PMqƽ)lPd${nm$BSU 2d!A9Jsa&I6&r.vӓg~>;%ț{1^yJd7:%"AvjOO*k3g'ߺRM2K yC/\[vΝ(OT$eh)㴷۾;!F58o\Qg5\IJNT#([pm5ɹEzNv3\Uv_,“.G8oʻb벍P{NvR~zJ6"KwGjG[c]u]^he!me`TcCu|5Jo-OCv4ۘfi*0zEiٸ8ؙQǷuReJ2Tv>Z~6]rVW5Q_8Y\ԫzМes\^h/TiU nx:h.c"7_=WZefj O dq[s*#}Eųig 'A7U~4}TkiFm5%\Eoc%gT5u^4B{o"vr ;o՘&e<ԈĆC>̼҉m:Ө4ZIhZ )&FDb)(iMsM542hPg 8-J2jQ{Ei53Hv\O4ٓN]-ǒQGۢW/)t?PmqcpdmgQ> 7ܝֿfU1.<|_EJ8\CWQL[ߪ{TXj*GnE"iWqmIC yWt'9l)m&l'Wlu]3*hӏa_lHbˆ+p)p-E6uF7Ն穽rEu5JGX?tfu<ӱ֓m Ku-pٻwٝ_]Nksf\ktGwۖSҗbAڍGK񩾬l+iٕMzJ}eNE^V=vJ͜j dۖmDyC47E8Ӓ|h՛_(yKWvj6L6mGs*9KB̥\$nFyNzVv+z7!Zy.*lLAWѯ;)Ey.ٿM/KZś L+Ü40릃ZL㺓%')>q.kHKj\DzK/YGcTzg(=W6rʟ/BgPhCnz 9ԷӣYVJzm7RIR&;C:Ҵ-$dbVMʫjg]8(sM>i7]uYΛmevABpvqdM=:{}DiHAջJd=U­`k~֙|4Cg{M +%\Q2']cI3>ʮ'x}*pҲGշnnie%&h< \͖VTkŋ}5[i˭d#CUڭKT7$t!vK;(5 0gtIg u]r4*Ŝ0:Rq(J_m'6)P4!iwk1rKxi)?@}i4)yM=MAuJV>Gn@5=/}L1]1.6y)KbGy)emx~j,,{Tn?vok^VĖ_aGg>fS:}ZgdGXqNz^]?:Ӄ={R ŒỸ ~TW_$.,GPFSMuM_nWsT\VJsφ9lbfoF}:YԷNvθ;'*vFNj/h<Y|c*-/44Zo>֮|~H.łwc\eYki|']{%/\ev.^-IΜ|gɧUY8N> j:^~eM8-_VELĴ׎vL[n5;PuLJeQ6 WUMz|Ѷ3uJ0r]#ٟL:l[_i%:| w_[W*T"*oh XOSthds>Ҿ/a:B㤴WW׻eיjeе7-eev׵f/.\0ƋnZ[9llH03=ԻYPϗq$X}{I UUSo7?rkMU{[f(MG2+i;![:򕽽d uGyXKލ:&~nNi).q}믟yd!*ֽ߮cyVX{0rVU<몮uUgy8s(ߤ^*mi}2;cn=/#8I_W%ɭ5){zȲKh4p!Uk]yh{9G+-WV#rO7euۗD|zp`xuye&vi;َx=VLOEǜ6:jO\jJsיEضE~ 뒌﷓-5,M7Pn;#omڎERR[K>*I4qEX>Uy$E@q+|j%12Ib"W#qȿݍu[+*EN Ŀ3+&<ybmJ|\Q{?Dž6UҎuog=Ƨ2%OZV # 4Jv}[lyйj]bQF\we_}8Dz^:Vo.  Fݸd·J[u|Q&)nFr$õo(wvw<=.zk7[W3rjX56wC,Uwf(=jtE4SZ[;STn+9\2ҫˋIt2%d6\[E:kjݹ*Y]>2QY6q|'gw[[ w|1ju,C g0?j'o>EJ'+&VbL&M\fvKKzbYI!!KG4=v~3\FĞpńeѸJ3/=~;) +c Ry8хM7^,Pd,I [IaQZDI:n_aR ;-ZrE:"GFI:RշRyKU|Yݵ>ogtR.ŖĶ}*uOmtK-xʿ Z~MUE,m+OLQMK}ONcAqYt6 2;n!v>iM~T[%kgG5N0/5ײ' itdtצhf4ӍΩg䭺]BuIVnvZdt[!}_.Mm5~k$5Jy{j6<=a1QYSĘhU9 49_sM1Kho%}TDSg5U-)ީc[= ܵq$)b~WY>O#6Jz+B+ ՍG~|S|w7e%_i?RVd"FDʦ1M%} lj,~~t_FMJ\YT 8[Q_\c(8*E~&26>hN2u]bL}\zO-giMk-ab.U4]#Jyx5S/2Y.sʹ9}Wgs+u}x&(Zi[_^+Qgvӱԥju[\\W 2M++k]T5v t%WEVSz,"BN:♃v2#yeX85;-K+ 9F/1hZht2m7yfWMi}dk3p ԸvX.)i/y[54RmN>bŏ!~"|$g:?Aqt|1}zy rqoq(Gy(0ssh_x=(d*6e%fEoÿ PuL+N.R+&јYd?"I=[]8,}y?O_Zbҭ~:cgc[ KǗT\vqi֟[-SLS[҅_6quλkq%-xC铰fޞ]zՇ\\8q0sj̓iܜn4QB9 ICE-'v;'H5<.mղKtǍnq>d=cFzm4ra-KdZ򲬊iYf>T# jw\\wD,xPj,84faPMHO BR.gI$tF|@`lG2"e|Iܶ/([&yIZP FNuFO~q_潏tE=ȩ{&썧-d8Wj)zoDHLAOSⳫS旪l/|\8Jj(2u* SXރtKzva2!pyJKL&SOW.)L3Ne0lRu'ߑWmv(p"><[-~7Y{}O5\|^8ijpKJg%)H8jm& r&'OevJ-I9]K&i)}K"nі瑪G~QldiВ!G彶sZN#8ͼ ;t|f@!+|xB]㫆5}~ MgU+c9e 5xJ̓z<솅(ۋU r͹It;*iǦ^ALYE)S]Z#rkhQ>LHJ.9j5sR]'bXi7\$!Tgunicorn-17.5/docs/site/images/favicon.png000066400000000000000000000033531216475616600206100ustar00rootroot00000000000000PNG  IHDR sRGB pHYs  tIMEi}IDATHǵVmW~33] @-Z"bbĚ4i-jX5UkLlHchRH _ h|B]~ܙ3<-,"?I&sޯ}0v{a^|iU[^m_2k $`=04նC;'ͼ:I$U Wz/3aG5H ={潧v= CH1w.u%q/5%  H$i]圕ְohHN2'I8 fuEH,9tPHErT$H#`$I8'@^ޑɁaV $ Qtn@Hx=+~Ϭs pQֺm_6/m]6sE x,U wb-{zW6NYæs'ݰɕkQ3l`?uQ4PfdcO%:&L Qˉm&YK.\F֣?Ӓ_zhLU F#h$/_ʛӛ]a|۵NRH3\##B@P=;$0wsRI J"@ݞ"xCAb#3I6܈$K6+圧EV7Hj}'C8Q0SBQm^D+9IHCr °]?O>`]QvH]vu=\ žZPcxTb.@T$(jq4p` Ο`ZZ\xxԦ31 zp[+YggV}mmvT<տo\y?&?uxg>ks|w|g/'=' e6WJeO_C ]̥?3k_d oջ5#{ ȃɐsܺaɆfA8i={TTNia$iԲML4ƼuO<}ޑ#,;l0>jgfN$b1*圙4ŏHϿO]>;^[#ɍq zi$C9VԦ/ԳH.[e~aFWj'>9&n*U0efL:#  s#hע!s^_E]3>'J;mEށ^ҐD S7eSԈkҴUƃ>xTr5km.CCpjg}gۦ4ASPqrYMNw{Mo, ,+ %iYk#_.ȧAI=p7.Om朻rrl"'}I(%k83&TIIpB$@YBSia˼ 8 W;4lvˬ8,0886ysZI_V&KC/99H,+W)=/J'?~i%8 By#N1>1r 7I#@YM2j:IENDB`gunicorn-17.5/docs/site/images/footer-arrow.png000066400000000000000000000003771216475616600216140ustar00rootroot00000000000000PNG  IHDRB21sBIT|d pHYs  ~tEXtSoftwareAdobe FireworksONtEXtCreation Time07/17/12ؘ[IDAT8cxA1@ bSCqlڣ~{ȁ."K\[U!<8" IENDB`gunicorn-17.5/docs/site/images/footer-logo.jpg000066400000000000000000000047031216475616600214130ustar00rootroot00000000000000JFIFHHCC+@ 0 !"12A#$%3BQUab< !1A"Qaq2Bb#3Rr$4S ?q'}&F@cs䛘X$˅+e:,1l}`0wy0RUn'L]iXq٣j*q]7Yn'LjXc% G#n9LFV\G[R_PyIK KL#`)e%m~>zQ8Cq={D>[Zw9v!nXoWaFHoa~R$LcˎY* [u瑷]ܕ&t{qQEQEgYwORqъLpիEO0SRF]#J*wY^)bxy#~ULIDP SLFꩩ_Nu9Z%O=Iv(ӄW8fYѓk'KHѬZ٬K)9ҭFηt+IJbE*{7i;p }+p*񼑽xlTFMᄊ`|ֱH#qc=`?R)1֙~v44S ҅k*?uXhua_xUcו,WG$o>U>{[SJd+ljF ؝ 8r3奒R#(@~Ė^l* n Al5EsEVQk7r(6L}bhV͉"ġ2&-'OE(ن vQڛ7T-[GEtQ]4.dGJwjU-A %)_ۥ $#KnrcoכoZSL9vE2[]$巏m'Wn2Y=ڰ ibŇe ,i5 ;,Fα"'BM3Ta4J')]奻 ɤ"LRXSA:vʎ^XL>6Y!ȧv6`s+\rjT\[}oUP]wG_\_zv_P$yY+?&wXS1J`_2oz3 0eE3[K Ksz^S\9h3;zC+JjUiReO?}j 0׿ @4%}A7$,V|.[%Xp9uVedmQ[ CT75F v&lh?n?'f>X~t֐4I F7@yRLufgDPr97Eiu'aJm"ٮIe}Ȩ[VlK{hVϐ7 V;}V7iY]1&*iNT 9ZzWrRD!OV׼yq:3#~]JNG6IH,  {Trkr5grK+ ^S;u-M@&ƺHdrڕ 7e_r={κU]QEBsEk\4vH*+*ZPQ0Q/5:yG[x8 L/طRxF A4GG֐X|5c`X:V#VŞbb쑢IJ ҩ la dn5,AV L[)-)ĉdeqP~Ģɇ]Z7rƠ0SDZթEI;;,I#wJgbuݡP1XX1lMN:N'$gunicorn-17.5/docs/site/images/greenbutton.jpg000066400000000000000000000005171216475616600215120ustar00rootroot00000000000000JFIFHHC       C  lS ?ֱyg8k Q `##gunicorn-17.5/docs/site/images/gunicorn.png000066400000000000000000000030211216475616600207770ustar00rootroot00000000000000PNG  IHDRNsr?sBIT|d pHYsuuDwftEXtSoftwarewww.inkscape.org<IDATXk\e9]n׮b)@!A뭡 11HQ4qްް ^bAC@(h hf Je)^PZHZNwvٶh?O&y/K0ní4{K ZoʐDHxaV7jOq2B?|>3 X׉!5M{#Lf1nf ۧ {܎b`J_Wz$ʴBKaߓƾ"Je3C0tap"|P0zGwcsᬍ85u_5-NwFYMscNe0aF D2e7Rf|_۱G b57p r`f,. $_[OMkiLKʏ=]/&(4j]O ,sDD,jc`"!)="d:㿅ᜎ?-BTkjRY` aeLEqq7r΄ՋSp0'dߋ1q혯y+iN+NZ,2f0fqG܄miѩ<&Iei8mM㖊%BYy٩߾hcD&`:/bIypXi{f ikf٨5(;֎_6j[ąW0\9:1h%e;A狌{Gj7j1$"RNiDf/2U\^LyxaǸ4+?FD_+ƋRy'4j/V%xwּfD6Q$h)cyh,+2A+|ӿ';^mwn(]TPt FFF@(((|||<+zVE؍?Z>xΧhEZ`6ruVUe7l˶]O2(]eY_P(,,spr( {yrXleYez>o>IENDB`gunicorn-17.5/docs/site/images/large_gunicorn.png000066400000000000000000000527041216475616600221650ustar00rootroot00000000000000PNG  IHDR^_sBIT|d pHYstEXtSoftwarewww.inkscape.org< IDATxwUOBH&ADRDpF<EttE(* !MiR8ҤK;HIǝOwa&윹3j``4Zx)KI/d1c1' Qi8 &7 k+$i46K7c1czYbuPF'eI>5hit#p,_:c1cEU8f?? %RFっ$ڬ1c1Rhe;qfITiabuT7ic1cbuPFCW'ggI>F2buZX܍1c1)*)Vis`a> ,ɯ-%aPN\Gǀǧ?,ɧc1cb N]CG&ѽþ;;kB?ͯUI\|Ec1cG4 .C`>|*p%eq+U+^(^eI\c1cL'jShn``O`>eW%#NہUC^óg) WVܨMc1cWbuPF _fnSwf` qK4J?]q>c1-Vi"py (;;K<`mj`׵x,o:c1cTbuPF0ēN;it swÁó$\u2c1Әb NQ֏t%B|vfita׮>m1c1hT:(NqMvMR<,_ڗty:{7F,ɧV1c1O5Xm< \+\ϒ\`O׫Vc1cL]h#/"(\q[dN|:c߫: c1cLb Nqcn pIqY$1c1gAq|8tzŏ$EIc1cG4tzY$1c1gAq- tlp鄪1c1/ViQ৸9*NdI1c1)Vi: `siU$D1c1k([fIy`ciEV1c14 8X\;Y[uc1Ƙ*@FFZq:MfwU1c1X:8VN֨8WYu"cL?EUE9g'T*s3ƘnY:b`si$vIc1BDfZ>Ō 1xc4 h$?$1Ƙ^'"k>⧼ oKcnmu 4q-VucL/+iP׋G"2.HbC{d>/U'a1P02ƘXBoWG|>NmNc5"2p0s>#c)YFF:y X!K7NS.YXXbN`5'0wv-M`<,sC:䍩99po+ccL0T@Uu (p(I٘&""`C`\r \ \OykLc !6c!i407C%U'bGD>|WNy9p:UZo(࿴PUۨ9c7V!NyWΣ>%2Ddv8wU}d MDm|U2Plc¶mhO__Չc'"_^KFp#"G=dLH441xa݀6w 4q}$1D~`oUNkV`W[uBjKUcLY:4kԝ#N8tZ53]پd (걽cd:8֫: cDd.9 X|:! 9FD ,"cDٖt*Vݕ7ƘؙաYڽQqq%䪓1Ƽ, ? D"0xMIفqyfgu??1&_׭X:+VX1ƼGD6Íy: w5;pZ*^>٬WSpKBeƘZbuhVshF%U'b 8 wӇ;p0^ګ;VAŪ0}cbgVfŪ?c4s_TXQ=?`` U=eB@UoWpg.ChϨ͌6U YL cf+Cbկ?:c `cUhGU>'"RlreVU1g+CzO4 y3Y'yP*TS3OWkOهpUCm:ZӒ? xBuP- _Sޞb> !D.ʪ}_ezXC{zLIqY+c;Xg3bNleTNUMl+6Ƙ X4p~FU1JD1c} ![Y5"ڪj3ύ1akVf: 7p.`=mVYSu2=ps}xr^l -tv ߉)U=RDmSOUc֨u>#q} 8K %c\{q0`cL$Ru"ehn\W[xS:xxxx=j́+ڹΙXC*V{ߍT83`'G |lKHa 8p5p"peGD{OTN"r=9UuTFDI`épׯƘ&mC{9p݇,ɯHp|m嫫-UL4Xx{ Sw.xi`W8NNΒ 2s{<έ/Vgŝ3'9czC{.pgIH%qm[a)Òuq >Ktdhf\4Հū̫ Kit%n,'W BD6b@qWÊUc11XZb,/h$4:BG߈hvNi!_ BQ8N͒ܶ́Y=ƛ; ]Pꒁc1&+VX]?%0\ꣀit#@Fy |-:5 p 8~mZ{ƦMP2WCk1ƘXB4Fgts*o=SwkitaM#mh>`/~].Xp[FeI~a }s7<#>Xj%"q,^SU^SEy8`<N2F* 9"23c%?-f W׼;04zW,09qm{a;jm|x77`Q:{55o5ѕyOq=bW=9%1MCȲkύyؤ% 5Yz_/YzJ 0qӾT'&tBD> $"ů 3No<^aFU}|NDV|\x-;EӋhP<45n'~'v>>buq] 1D#Yi8Oc75pHoUi]UuM1%" .T/z Ɍo:NkD \\kf=mOSn;ҩ{q]iINneف<\\ճ7j p p2pe]"2̍iETDŽ $8U}`c7dR;4vz)UYLqlozb7j4X5͂;'uFȒcZBU< q͐77ឨCbc w n4HD=kX~x=#_V>u+K7?WT-l kbbWc"r pjתp+|,ڶr^m q߱VQ?.f}7~+"W ikx58ĭzSe4K1S߼bVq D:䊟M"\BÊ˴HDV`'|:o7e"{WDd%ɀpS$B[8xPD.KfDd=.\Nd䇈;v"aF2Ί{{'[:<:'K Rlr1vh몓1# 1Gp&|{e1@Dƈq~Nl{E$|ƛH}8л1p$"[VL;Dd!IqgR9p("0B52czV<;4ŀE i'Ȓ!>~):Ɣma8Ț|W*ʪHJpsl"򍒯"2VD~E> +"U'3]D~k: LUD()+*dUUD[ڙ!p[:S<ĸ Kr X5U OF;e?BU瓯DdWY* TD0pE6Ev}^zwֱ  V7B*.VEd8j~ZBD>;Z:讳S{X5U[ %NFmSĬZ'؈LD~W4.[Oq[ }5!+X\ó[DdŪ$"뭲dũbZQmP}7",׫a:sl?5LU$|κHN%y^V:87N_iT'},Yϱ2.V'㭩 ȀeҪJQzlOƪu8,"Cq5u(VB4 =;?|ݛqcdV4ybYl@{[$:@JS( cfd\NaQGbvU l*$"V*6:a|]DTue q]W8I*"'G{:/fzF5+,"umx5𧡚.5z$7s>>`,}Q.SkSiNՉ`2޾{T--lՉ^:MmLEHi"!R+e]3ZmS_wlSVC p&4Jl8}OYچ,͒|]܌O@X-Њ괞 ߘv-[aݪD#.(|xzsfTSs'7^D䛸㺜k+BVn\."u3} s̳+Xԧp[ߧ4CȒ,ɿ%",ɗɒ|,ɏ̒xkӮYqQu"},TZR=\91wgqWDȐ(VΠ% %@A-F4"괿/o&dIjoUpi+VM]>N_i]0+8Z9cGN&DdUQ}Ed߀Mdwkv,v9={wfԌlܸwY\V 83N^ V+:0??!">"{-91Z蹑_"&pByTHwPٝ_/'"ĺ8lZCqFbɬX5M-mSkH39nV3ߩ:DdQp`e_4L |}`cVS5} ū d(K8^:cڰoFgI~qՉ?⿫DU}s\=O_OLo w˪:Pl=_x r%_'b{ޟ>r"p(|'ׅ(^eؓ:dYHf4'q_ϧnkkq`sZv` ?pNpt;7!3 ;ˀUu|` {MJg-b&`ŪiQ q%KU'T9s1_TDNlǼ`oUrFQU_ĝ) 7.$̸WFHߡ,@U:O,攮lU> ϶CYd47p~}̆k|53_9r jUU=ePշpv]9l{X>)"3Yl|"k¸'TH8 w6nqw= 8&~WlDU "1p:w' Z"0njUt8/xe~EH`?)r+nrq!+[ n5 xN3\@۫j[Ez\;tHĀ9jYjj8v:^/oE||OU4k@ǶSNKU3P7WRU^1j#6nV^Oq+ ,iPNQճpA l݊HuU'ZTU=zzk[lV&;2NN3: 8QD:)"3[.}mOU)ifziT>ݧ4Cx*"sL!ʪz~ @^kp|tzOnz$Tܿld4@5fja`}U}CqۃCX݊fb4قAU'Tu*n2ĹE$"$p) }TWbf+s}po;ePUu'\Jr7~YU5UwG<(v;W'S]TK7uG\V6U'늆 {2灇Eo"iqDd&E =]kOLD!nՐ^6Q#Fvc-}ըS">=Էg TGU!{σZ%69RU(|0fŪiYqML`z1~فD^DDN+D-ؐ3ܹyeM˪ރ;L?θy W2U=h˫Q!mBgXiސ0[_KK*O!yn+V1U"NVD?P3peqsv,ٕV9k30~sJxZ޳^U x(@3R۪cyM5 @xkj^ux8Ɋum܍XO!Lo0[Цu:n4OJ!" HUq;Vt]X%YOYXs3Ud1[FUj܈I IDAT!wz U1 lWuBU>k_J#pCWPڎg]:U :*ٺ?+=BUo 4sQޚXỰՆUKޓJR p}ڪ6h%]1w0ح5{ m+&9"q1[Pm]z\Uo }⡞ưٚCΘV;+dJQ?dMcݥt<ǫbl&Þx("2ǐQEdߝ |w$xRՉE"r1p"K }7bb1(sU^zD>ϫNϗfSEdT_ U}x8^'Xm8FT1߳$:Sp Up>0?@w4DѸm{䬏2?O?}V6csW*F'\Gh ;\TMS3%_bL,9ޥ5ՂcЀ],>gy> +V=n(N@1u,yI*Y wޯՎ^V D٫UucM|U|M/Vߨ`7H+V&N࿕1uw ">Y#+le)X}YU&^W>&vhzZ*jhCVe*NŘ|&N5NWS}7G:WܚʪSvr׃*VoSYڞƿGZq \ [u.ƔVW=@o+"#:Xe+X}mbg4cYm'q0D`֊1fF8|~Y[&:cVUf:UUO+>PTe^+91pjM|⁞8:cp1 G;]|R"RvƦZ֍ؠ<*Ʈ0试U]fm{|>*s,EӷCN,oXeŘ!|9K,ɯ4iMwMFzJW5gZfAfMX{t>yǗ8NJy{b4ɧŘa%k'Knn8s_ !ƷD{*+eob=T'"8.Xm\yNmejbeᚷY|2݋h/X#%S[gd_`k, p=MD>y Y&n"p|g״3k^;$\87u?pAYuleev+VDF?:cZtP!K4Vk@qg)^uCDN<݀ZVuOc|Uȼڄ1V(L^#zNjmX1wXj/ qյ8uNFv.>ʪ+n WV<*seuk%U3V:~W˷xwx3?`j48ض\խ|P/34:)iH>W2,1VŪ)Ǖc$" դyN95/W@mIg Grc\B~ս<66k2*߭xs54%hQ*UbLj 8`q{MWOD*U5 9RȼC.MT~ܼb_1^SݎFK;iuy{xX-AFPu.t,j;Bճ#P۪ΣF|je<^UIo򽺺9fpǐcpgbOv~ x!X ,Np i8c|hF4K7§4}WQu ,VWS;L޲c1}]<?>9D 4ia9iH{D Q=EUQu 47fRc "=b2@ոJ?iGlGD wX"y։zX,NpbUbL ktyoL`ZCU'0mch߾,9杞Q {ϔ+E*[m?/\+" tg<ǜ 8SDvwH"afOŭ֚>eŪkU1'eI*aΜl=IUT'fCU -?+V|+tQfTu*\$";TAwu"mN$lQ7pxYPm_sQk7 0K 8fōwȒyy:h٦u~otYZiXd^3N4K򶊛,ɧi7`?Y8TvlHk7mOSf{FM~o{mVsTy,=lDz ݁D1XY8(@N/ ϩ!tEv?SծWsEd>`K`{`{?QD6vXPS$l-"㾆B:XU'ѫ$x_u.q, , HUfi`/:{~"UpVC\WV ӁoAR2_;,ÜXNէqM "V6TWDd 4k5ĥJkP"s//5 `'yzpN铋O  ԍ?s;`u^Uo&!"ڐ+^Db\ggU^DdNJָe'qIXRFnh V\I qO-3k&K򩸕'4 &ޏ,֛%q݈Fd;i,'Ѵ:9lVC+=Pr9­jwi!w, dׁ9(O*vPa\SsyyϹq.D91> \/"?P㺌;˸⡍=pCwyu хUi(+VO7Ɲ 1Eԫ@U;-VGYs_l 9nO%qsZ rU%"V1CjMU+p+9e۫fjСS_ڛf7 (:˥'>ƧĪẐx4@N;}ޞ5T۪Nn;xu7쬪W+V; w}ų$'K'=ļC|*5v{xwXY7J*?hv>!K;{1N*9{f ,TQ1buTAPgVNڜXu.zVUx~t}k݉[=[:K$1kЃc &#e+XOXȒP\c*,HggB~oHƻJ6%U}V9sQ=0WUbj{sx`KUz3nY|hY\[^ |Kہ-.Un_V&_9TU)UΩ+V;^_$?D/ធ8V|*:W䓀ps`67#eſFFOkGDDg_ͿcŸݽDUU'aIU@wb"P*T3vF<} 'T/+V;&%`,ɿ% 3/Ȓep1Kp]8?YF_lsN`N}?@̺+0[U3g{u8*#0TR`#9^57ʲ.vVsqۧ{cb3WeIsUpJF)ZH]fo4jyyoc8Dd#kfv\jgz E퀦n@frՉS[O;w͟ yf@M$`{ LbMEvF@ EQgva8(:U 28U, ˌ,+t8dfໞcY7toU^*"\$u7n^?l4ꃸs<}]Ŷ &쮪__UVo@q% b撮ӳ[q۷RZ8j"~p7Mvh瘵#"~=WMi1bSqʀuU#Gn> 48 ZU}Nd航ގ{Ф_wkۙi[ @˲$??@VI9zy+pVV"pW>v>!K򻁟ycܟmHwe(,<{lqw4Һ|:p RUk[UQ]*pSu2yXmbu*mɒm<RqYuPוU$kskd+vhv>!KÀxq{݆]~>⨧8 :IUw>x/8kkLT>U" ?@ģV=RUOpۦmLz.ywRuXհ)seQCY?lmEgf$5}vZyueբۀ%5w<2iv?)KwYrzx1nTu [v#I z5R2TU=װET;e*n'TuU=+zn潥xm|1yđ;Sȑ-6jmɒ/qFE~~R?Z]jFgIR|9)wVph>:_e衪w}p3 x뀓3TzרƇ2_L\1Oo_P 11"2-;.^Kxr\|>z)V9q9uDbZ,:W r 7yӮ#}50Pz wz_kqI9+)z8vsYq͂;GF4X.K;8d,ɷ2F]^#:gsz|!"Kk۸L>)McqEfuA`MwkṬ1qpyk^um&tLVVX{ISn>E^1t%8=m[F43ط;,_86=iܤ<|l #O.[3Tw"xHknt 33kN`:nk௏wӍis=Q'"cpDZk,nt<`#Jj %sv1S%\,N ѱgiNo8+:}EU 39T.?<c1KVY>1O{VjBv^,[u@; |k=h{P*tr>#c1tϊ,VWy%=@Z¥(Yɲse]Ylvq"8Q})IDATn+ت1S+V)K7qg|ǃ tZ:6ZF)rh7'lѹWx1m'ݽ h c1nX<&K geu m)[iuř`$ 7=䘗]Kz/nP{/ySpdaVл 1&+V;X]=Nz?UJ:YBm\bS=9FDV+(Y8FG'#c1djg|7x^m \Y~p,6^ ~`qɸݚXDY`ۨ~2cOVv*qղ`˒"izW+͒,>3ڃ> f_Sq!|XW)W""n]|AU1c|b3OydFuan!WVoّ˒\ӥ7=b5_u,"_Kvm`+UKRc Q] 槁۵,'f0Mje `,+ݢ%ָ|_ӨVEP/P՗pc9TBD"ԛž1Ȓ6ђv-K򧀝.ȮqY?(v[;S<<2U=8cqu"Ǹ-"?7{T3cLpVv.88V kY 8"<]'g1ƘRRu 8ɒUu.%{m5cάv1pBFl͟ewp+p|n U,ɧy 8fqg6 `U+T1ƘfS8YZvN(D3_mi{B=Wr~yZZUUN( #U}ꄌ1AF[gt3٧ xzWkؚ7 ,K ҩ86iwӉX\W9+NLJ\wU1c?AGt]KV+~ <1^[sXeCq_GSU}SUpP_cLbՓ,ōu).+B_9VРGp㐚nOqǴAUWo߇N!XLUwW{Nc1aXQut eٳN.o BYc *YbZń}kp3S\Ӥ5ThU}⼌1,U'k$O4z8eIhݸ7Gq6Oh,/+W`jQwpgy2m|nNuZWc1}Ɗ$?'Ǹqg|yx^dI>1Nˀ/x %|\)Vi$.NWy |? |XwuTk'M/obܪ;\d& !ʰ@Hhdi˪ʪKRU>v HPKURPR`x?jihvHAYR҆]Bv!,Ix|=̐xl|?5}s19~xǧ@DDDj~v*{l{U~aP7B\ޮʞ!w Ã{[#Ȏ(v+4RC}E <~0 5&""!İGsO;4Cc{б б|7-KAm<`aR|V? DK+Fov5 P6u0W,?0pWs>Bujˣ! 񾇚6u4W, ?_n ߟ# *D[nj!Y!@*CDDDD:+Ox;ύ;YhXG!T?3(ޟaRauמ.p ;W,n֛~ʿCPBV PgB}IDDDDԁau@e7W,)vm>zPdg(5{v ùb3HY%"""O \|7~4wvvV9'Z1ƈ^1""⽇H$@sE8ֲs}_xMs<K烽.V`cW eX~6cо]O/{5NEsșz:… zQ ճʰJDDDDV{kɁ&Ib(9㽷"bTc5U}u 'O8aDU=11[[Wʕmw~w~I3c"\V:,\o_U\z^վ x7FȮ?6۾\8UZ'"vks[kUթs˗/rG1QmʰzɨVEQEι*#UD$G"j 0fY. 5tcIي[9}v[%|'ThC9qWvr\MdF =ZvC`BU' Ь(J5UUcs=_>|!hV &oOl2CJ\Mea/?3eAu.DX2ı  '"S/0}TcUkmefff1&"""%:;;+`Ne @){RGǹ`SѶrV+9sK= 2?F8Gshvau9T#>)JMU(ᵰDDDD}={T{>;;ɍ㊻*o7&{1&cC0ȣs#w)ı-"4MT*9#GAFdADYd Q` ]upI=;e2 &t^klaY'x ^ADDDCRyhx#OܗFJzNhUQgCϠ=?30{1Sҳ͛7{:'hy9wVD~3_ҪW QˊճcϾVEWrWC~Yd7̵dm{(w`tΝyW6IDDD4Lz*Ǫ:m٦-u?$pѣ_km2A5t![ Q%BjV2,r WeC<ꠏeww dI 5 {)H=U"1^x?CDDDJcکFpBݏ`և7?D?7?7A_MHo xTiC ⽯#Gp :Y1viN kwy?ovaUU'O+6@cOd5ρ#,+kmX,1mjYXXɫjAU "RPP|c9ҾVF<|ҽW+n+& _XVUj:疌1՗^zi%""")ɓ'l6 m6 g$QkwGd{6QXU&bȒi_o8sLqg1YUm2"E=fVa lw@a5E=xm!4F=bcL ޱcGr~DDDDt5w}73Vm#"Uv=lY>HQ~H)!4UHĪ>9&7jaϛZe2y#}dT5RHD"UmVmlh Yhfs]{[US}:66>ڦ !DZqYk㽷j1{oEĨUU#"VU vkwS׭r{3D:^UWuUc SU'"^U($r@DDDDŰ:N8a$S'{H 4Mo־{\ 4Uc_}WZn_q sc{XVsY 4OXNeMmux͵Xw۬-"ki޷7j6a9UUcZ֪jǚdTDŋѣG6–'kyIENDB`gunicorn-17.5/docs/site/images/logo-bottom.png000066400000000000000000000061111216475616600214200ustar00rootroot00000000000000PNG  IHDR>**7 sRGBbKGD pHYs  ~tIMEx IDATxypSm1xemRp I6&=$; $mLwNNCKˑH "0 ezrjfg%hfxޓ,afcvVYߝQҺAF]Wc{yu د?:Q> 3?|,I5F)ɦ#/ݲ3Eö)df=F{ֆG Cj#lsN?qg kD/f|C?AK_1EdFAMze7H]eOK&FP=m۩±kV}#m|]wC qT Bh`· |'/^R55g|C~ީ}{Ѷ۔3ܟ Dsy7/Mhhxr>Gr$qu*Y'_ת^gܒ._ҮEv Jö  KbG f6G~Qg|KCNr @?7hvgv}LaŪzGb+iYy']˦-|N1li}hH2ņw+zf{>}+\6{ 5gqk༰zџ[zqND]gs)n|_KH/O=|-x:R0kK󖈣VJuj]S fYiA+o}wL b6ZϒRq —۪gyֳ0{a3$,X!(t7{ Sf-Z?7M'-Ccibierq0*8]Nvq]Oj^3aoТ`8_U[sƜ|ۦO'IE+-V,VQj F Y,s'y`ڷM} \#p&7LV`Ήk/~v֚fb%ǜÝcd'pf﨧aaa+l'MMwݟj)ԺZ~*[@5Nrz͍j1ږsFGOQ%)lc8ɜ-fQ/f%@(0Fu+G]UN{JRd'eFQ~9OegkIsNh,[2+'|Yj`p/w4w5Kyj՚6Ѻ|.54S*TG ~9]N>qpZ9օc5q܉wKƤikEZ|Qi,`)p $u{N_=uF!#4~š^Qg$')Hnd(xk.w~w.d4O9oFƌ3OB wb4E+ Y*G]5헴m1}]:߫dEL)0LXFDc`9f6$R̤L䔼ns ; t!V<Wg0zs7NSкdWQxjoF|]s&o3_t}g=r-|n[^qԙ=2sS)UhP:6`z^ØK̈X uƛ*z]<.!q #G,0>,& 0~Yns]ڛ׫A?L1+D d< ܢ=R0pcn׈,C_x7#QsDԺL- >A! _ s}bIENDB`gunicorn-17.5/docs/site/images/logo.jpg000066400000000000000000000237761216475616600201320ustar00rootroot00000000000000JFIFHHCCG   B  !" $36a#%&1257AWwx89BIQ E  !1AQ"2Baq#3RSr$CDEbc ?tN8DN8DN8DN8DN8DN8DN8DN8DN8DN8DN8DN8DN8DN8DN8DN8DN8DN8DN8DN8DN8DN8DN8DN8E旃$!#_.;lCB&12xQHkm3s! p6X709cwo1T#& %,N,S\dQu;nvӮ^vWK̵\bF\˧uY_%BDOGtVy&[(\iHAds9Ӟ,'R8汣iA;z'Mnb͍R `ޔTw~vʝHj7Ԩ[VT#@rnSo #2'=0]M#kUP5ϧ<:i"loZGɿi ?&Z}[Pm.Z9OAS|bWy&|c,8̍٧`W=@3CbQ .aIKF#6`O.ʈ,L%tZkPVS`;%  l G$1= ZxGYhԨO1 ]ρZs p; L"ۖB 8GK<|M:J6iq$"4V}Z\mJB!1 !v!!&ȐtqvP $3#DpII䔓N8DvF.GJ̖ͬݎ5b:dpki@Sk1 ő1"I[7`ZOb+5ιo%0''<{hb=. +O0[w yn'"]!`<(%g⎗!|s?#IB~yzݝEN8DN8DN8DN8E} ;sݱU ^ӳ'oHE|r36<%:2G:'Fd,&&̋UNxu=>D3Gb8#C�.E:!kzN0[խO-–G pbrJa c|mh 鍒bb?SApehxײg5ᒼW\&Y!I8$-*iٱRy&X@oc)oAZ!tesW١*` =ݼ\IBͮn3HMBaZӖ$>#)pnR0ȑgy/UzʵM埍h~ng?C=ѶNy]6f|1ж|z_`KBqTGJNQg, [q9Mץa߳Z|{w7o=۪ ~[2Y Մ{3$\*-WJ+>RZŋֵLOIoMY~pR IJB~XqNIye2+]~I8r)ԵyX-~Sȴ Oi=y;tkm?*. NrY͆m5.8:kC"IjP⸌̌BLH1LiY!i ?NZ8go޽ei7` F-AU_Ss 14(B*k1Ӟ,gk>ي慛@Ű6פXB9.+aTfa åGeעu֬{󍅟L:,Yo]/MܵKG urBu"an`096xqZXLPדǞ=}K#M-ڰ _K/n½[{ f~@J >ܔ76?#3>s9ulyy"SsvcxNܚWQ h(vYW烃u* > ^RT)8ͫ B/ϥG^7=z7tkaK0EF WgAy%Ӧb3oIX ]5Dkۍڕe ȋ < rCC,n(όl̋YO/Ju쎲ܦKϬɾsc<,[އ/1Sx[..̋`6NԺhThR gdݫTaDJ302 91q%Hebw4.6~Q.óXL[e»)ת10cPEgeQUl!12(,hzdFܕ $|:V\vh*V8Yâta4bͲJDj"t4R؊jk$ӏhX9ӞLN8DN8EƼ[**efxۭWdu<% K !M>ZL[Se ]_T=+A=b<0EI@9>G'l*nRūu(Rj1ݳ^ظcjhdCRn|?W\w@2aL3{lKravM$1!zs\SHSVS/.|Oq.Q8| 9X,'w|e L@NVs&&>drwv|g8ZӄNNtNү}VzH"$ npZ DBiCq>8TcA6>[Foh7(JQ7020)@$n2sNkMhIn~Ej>A+6ތc~]=W6fJq>6K%Mqabx"o)Rg?]iZ}_NU|r[,$ә qIB*qk4_59FEX ۧ6$Ieċ>,3G lwL-ECjfDi1J~;-m<˨Sn!iRUs ˋ_O(hY}TpQ2_,ltߨdjŞl̒!vbӣ|YV*HnvGkCF-dS 9fF =/ y" _jyGp/I<ĝ̿o-:٭/GuGq B\SsTJ\"p)1Hm XMF!'9D슸?f ^4~UU/?o}'_?:(ON4m{6ja08IȚ"?*|m52=J1&hbrc+džq_ ~ҭLQH;7 =N(XQ 7!Bpv!gQWmE^r"u퉚֝ėË&٤ l*y76("툓ՏlP<m:ֳ݂هřؒT#r:[`r]s2E7+3> Ga̶3g3(]6}JIQm7mMU6*hɧkm(@3}OTB6ͧtn(j dN\%˟:TCM7aƥ01LI# 8 F^y^<0_DSJouZn*f9ݟC)"{MƠ3 ꁨ?p=GPK؄lR72 c;<Ҽ\gi'ut]w ^Ҫv H3Nh SK f[!,GBԜrBl _ ?¾aŭi:v{3=CQ~YWv՗ͷahwZ'l<2j>P0`(M1Ɵ++e0ϸj~Bޣ5*hi@v%e3n>g'gz3/{k.-ŋ uim؊awQ]xإt_|35VlFV'erm@*$gNK Bz3 m"n}zQM\ G@NU?m6BhǵE?(;H?@E[XKd5 ROvARh/N-G i-JˢvezzSzݭ)֫eR~_/ šӻ۽ou;! ՇF"buH2 "@SV:M 06]s1a>C0e`R+O׾">8fZeDmb\lu}J! X>Y \>(ɄJen'll"NurOޱv `//w=>"ϪLw.*F]dk0 ɈL cfo.?Hje޵OQs|CH G\mNhi ~W@%N2/;!3[ϺeN-J̿?EՏ,8DN8D+*s.]H94l[ _[p.=Y7f%%Hة.;)o𯈱ڒMِ%A-tS{MYsqhFVu?T%]9#7&}CmvrE DN35F;83ūWAH `C1,Dapl]|,HPokTUxxmR@0xX䌇 #ZA1WUjUklK9%ť-QIGh͊]/ǵNXf͵C ݵ@ԕg'e 珋T3%Y2uo9@؎9=7<=MruԬizpfBWQgHl~mCus i!)gD-wcrz%L/'x`k-g՜aFr۸Z_M [ivzs7~˵i7LX藪 6cG#¢[7Ǥe755JTDVbR^#ׇ>bUμ$$ 쯈iqqa$q a63,UN)XRIaXd^1o8o Y{ָѲ]-B;Yi4!qyiiɲ1. q'?tJ'S)ư= +5 ,cy"~&ZpchDQu#4Ŷev`^ʳ!q_2.7Hzlyn սL5En36Y(dw"Yl4NDBe>_e9 z1;2)n5%]?֑Uœ 2^ U4W!44CQymNu]Z]>G\FՔmZ, G+>3dG2$Hf>rn-g˞|N_b?5W`gߟdR{Rfŷn_o||zƲUŁr:erY'& 9-Z.X˟g2/(\:[^l" 'hAlcs\HFu=O2L'd_=-.sh%5f)C5zv0 %cRC &]i+'tR^mU Qv}*)0!ʊ6K!(_BLYݟ,KZu[WP,X'e_24-VRc EmJj2iJFf7)mh9neJ CB.G/ HyfbQ' d:ߟy"$CЋ.&_a_쟴> o}G?ѿ|/2ϞsµҚ qqSVQhX$n:f4y(!-21.zDE;ҋ~ ߎ?_큟~KI}KC~7ݹ}]N8D^\ʕ]FsIc\mgEէ>~z>26>I=?oֺ潰*5y[hJIƑHʍ_NìЬajhYF\څ$Y' όԡmF[H\+0b-I˘L7uYzn_dzm~߱]G]ݽI◢u(brݑka/"&#!C N4LcGTgsJM_u[gΤurW#K,#yYI2Mwq S_n֛fmF]BZ1idG+VeDB#)YrmwmqIFu6j(ADZ]1\ё^]"mYD>!,1odLPͶ ۰N7!뻦;-8nn.Ỻ7GO{Pm匰p]1G- w람zLWF QDb$J"|F8Rn!i>Sg.n:|LZCIoch.lHsK&Ƿty?^4~k Q%Y<6s&BÙ?^۝٥ʬkڋtH=ȊH_d{)9!KnזǸy-X7C⺔kr*eRE,G+IfB?GV+q 2mB<];ss ,/0;qgK9ubuл(HB*@Ke)Ba^Q &<3eWFq~XghgɆ(Fv qǪWZ%alAb0g`:DS>il?mZN|px 9Hom~v\B%'8ą5 3U6֧>bgrrؐ"KwuK&ͭ^ϲjHimEc !/QuTfBk[ǨD|tElQƃZFTpGhy[,_22]B<ʛ`6VfF'm '"p'"p'"p'"gunicorn-17.5/docs/site/images/logo.png000066400000000000000000000162571216475616600201320ustar00rootroot00000000000000PNG  IHDR;qdsBIT|d pHYs  {tEXtSoftwarewww.inkscape.org<,IDATx|e?d ^)b C$dgBȩX;r(x*)  kTPRiL ZBA~ zP~`J[&;ٸ<3Mk^If_|=̐$4g֠$u4i2q ?LBp/xrFIf Wl1uKeM4* Lb +|?Lݲ%g2lvWݲp[/U)4i2%ġ pZS niZSVXI& *Q"IG~`%zG1.Gx\?6`m<@r_$8!bo=U .nDO*.]axġfQ]\`-S.o@m:Aq29D4Pf0~_WyeDt_|f.DfԂD2g78. "X۝ptvvVK@k* 7|KIhP阮a|rt{BpM,T&8 IeiguxI%D@f%;yf̼6}~UU?=KD<=#3-3U7GhֶaÆDt%j!fE?c+4B4B'1%3y j{=̿0ocߨaL\:bʔ)p4Bp4M-Ie 'OGư(9j~l۞;v#yЈQl`ED,0h_u<"33ۦiZpfHDklѼ5h$zt+ 3_0t:k׮suf۶aFh:(b05)?OD/3s8 kf<*E8fZHGrgčB\.w!H%"hZǸ;f̟>eqM#f#>%NcxU0d&S/L A``Ngԭq5Q,O#MWQŷ-3/uL&3ܹ\.J ޲EYf6S$3b֛n9n#ϚӍ9`\kֽo(4MTcawQX3@۶t:\4\-[nYfG @wwwmM-\0=PkN4[kX #t[78owr/Mz}@Ӵ9hqaZ/ÙIBDlvD@i*3Svk6[1r&a4wI宒kiuͫ8N#$kvI}RshgfPQ֩TP"Z )pR@@DBܖ~=/E$3 X8 "<'s+b\VWW"|6QIQPU| s$z}C0\#*{ά`Sd&!O$3LnT'Ff^ * 8g g * 8/;p\gIǯcx.Oaf>KUՏ "`yY8)|QUD_#L3Lc棉i%ޓtz 3j]v,ɹ1PUU=ZA Ep2~U *̗\{{{Amx32qf sK'r@RRb"^=:y k 7hIkkֆw+I!VZ5d@ ""[φADKu]_צHDi0@0եN{Ʊ'$wDѹl&MQBp|iVF"$+f \hay_)uDlx< g.f\l4==g;juK}0.~g+Var,$VobTrZrF<|vؘV5 C5 pj^oѨnݽk̙]ypj$W,"\i۶>CrX,iݽZP83_ yQGWQ!IݷW2fyaua, YJӠ?\"mWUu$VfB:BYx? 3%zNر@0f~-@S;q|zoͣFUՄmۏ2ht'c# -skp4ꐎEQꖓTQ5W $D"TC?jeƑ$Z G7Q#R FӸiFNf~(yo1"̛lReCKKK_&WN|a)u{|śW/QOXYUۚ"9[AKD3+URYS}G2 g7tl"a8NZ%׻ᵆ㫪z,{ 0v~t:'H)" 0x*2ꥊ%E9f" {o e(ʁ>b|>f%$8t$&ߘ }dd|f*)BJdCٱc688)9GR'!1mBshF8+U>ACݏp7^u?CA|ϧYL"̦I i0/SժjmDio5f ᎀnNك] ĊŢ m0󉲦o^ZQZ-5}c"8đpd>'~T8f66ֶSU?RUokvvWWqҡ&#Z„RBD܉ոuSe-s_Zu*uY"a,iHfs2?`CU~UU4M9J]u7B38,{x\??0 DCCQ,X!yD*zZUUݯ {t7%NOB\s5 T=mpf.̴!4IBd&Q 3;}0yDt⡶mRӴ~ɓ'oD7'RBR#Dk.)!f>X,>񥨪:Y Ndam3wR!R##p2yR,?hw/dA~y653MDZvd9fdG2ms`KLb5oN:6Y%)Hd:18>翣fΜyMy Uwww@±D,ɤPfh5McKB_z/=.xaѱpR|>tWWžU4*?zgV!cn2a0Ӕ[g;;(Eq/PN\JWٹaC ޅk`*oR\ m_D΃WeJ!c5lUU-7|ji5?2Wq15,zNx "z[ JMnp&3d**xf&Wyiw3N21sw,yŊ\D#X,@}BV@Pd!Sd&w̞|[fMB廦n}q-A S Hf*9n/p;4u+T>Me F۶O}AӴBflf g_Zn5 cQѶw(R<ꨣDc"ڏ;vl˻UUgQanSvs7kZO8tUDӈ駩[F2<m3S8q9UJm %8. z[0NU(^!h!!V"78ũڂJ;MTD0rP׍**gG/wr2giQm ƒ{W6}$O.t$p#nVep΁c/q\~c^T(o3Y`$dq%ܦɞX !I)aփWp~zS0T0g 7Յ!Y4iRN#s$Np_2,d;߀c27I̫f@rBq+&[xu4Q0~ZӴUeN-~aԔ[YU I}oմ[i铙Ր+CԭU1 WX.2u*ډ1Az-UMaRˉBD3G"׊H$ն)28|ȅf9E۶w=F4ѱ#'<- ۛ+Ȝj1u ^oTIMpV&~.(0Ey3*8ެ Bh8)48Lzޜ W&5unfȼY2uӛLQM )n]co ʘd3ui8)ڮc%N}5)}ʐ V921T X{qL6fE9IP֭[2s#b1 BD!D9D薾W]{|YDD$ fvi3f:D6OB0eClޠ'~e˖-.BBlEQlb6|T0,Xpm2gрiDr9m7ׯFV"jB !Z@+32s 3 fFADCN|`y_ks%^|Jd} VJ- Flsr؋fz˗=W,1929'6j8hҥ,5 DT 3Z[[ {nMܘ+cؙ'F$ȜZgDԓfE;MDУ>:@6 @k4Tz0BXn,p̻! #6a#lI3_ 0D9W"lvP.<.]j(;y+yW\qonE9^QMf潉ho8y"*6 !)t45 6eo\/yߋ=Wml"&el2M4H b#6nn\F$f`?f7.y(&ME4=300:}h85\iZI y(sZT~-GF߷@LP',{T6Dwl QUɻصknwIR{X,*JD%Ɛ=b5ʏJׂˮ|tc[mSL}GAתde f.(0s^Qhmm-,^5ihj]|9͞=;200PRF9 ۶#"B5ZVB3[Fvε/Y]kB YPJ%  MD(ԩS 4'u5"IENDB`gunicorn-17.5/docs/site/images/redbutton.jpg000066400000000000000000000011011216475616600211520ustar00rootroot00000000000000JFIFHHCCl* QRab!1A 4 aQ!b25AVqv ?\q՗gxꫥ*ڳ&9c[v7xuMW÷mX(<@9Όq ӞB".'^xؤk3oT-w`wGD{q`wGDص}fbǘp)buHɩ 60ʟ6\&)U ";U=AWjUSsvH.$iSi!gunicorn-17.5/docs/site/images/separator.jpg000066400000000000000000000006701216475616600211560ustar00rootroot00000000000000JFIFHHCC "aQR$b"Qaqr ?sD+7פZ6 Vl7Љr&zK<O_l _ &jm5z a ۑN\;<"pU 7¸vVW+j~^!]#ʡۑgunicorn-17.5/docs/site/images/title.png000066400000000000000000000065721216475616600203120ustar00rootroot00000000000000PNG  IHDRO: # sBIT|d pHYs  ~tEXtCreation Time07/17/12ؘtEXtSoftwareAdobe Fireworks CS4Ӡ IDATxu:?k+V`V`(Du*]\\X2I E;G 0| 0 %Ocv}EDl.$Y+K*@Ƙ6rhJ3AJw~[[cLSJ3j%-*!{đ9Ub7߽.XU"r,3d GL;ЏXU;ojSđ9 kFbHwUtS;cL `yvOmD"T*1vQ'cZӐ1&Ŏ}3Xa_o9#̉EdOy'*my s&^sUJsƘ؛uaJ{"3cˍ/H?ۚcyKN̖gwzpn޲,Zc iCVpn֦mډpul"[E$aؠ2O#sQ (ۍ/W74^~;đn2ѩu&{D-ͣ;2. v'w,NY΄ bl&3VY"=;@E} !:gYs JXG΂눚 `e> o/A[an$9e{ҁ.h;jnj3n5Qf1& heYrnik}pe4#k9~ؾV5l.N(mfkN#޵Kx4(qgr'{76~kYg%Dd)a5D*Ss֕2O}m&"{ʾ[!FD@Y"n=+m{MQqov e|F]y ;zM[?%FM%_nQ{oo-genדtLJ(fM~O>c=\bJ+l=g}JJD7sk+!X6$v{gUj.׵X P%`;{JZ!K:bcUj_ʜ]'Mu|fM )risN7Ez#.+%vJߋM׈+-ہ0Ҕ)y)_*޴w-[rI@4"s2o1dG/H\g4OV ߙ5yE['Nf'^ԊS7aM}Vͼ:Bx;/!CMt3w^ ]&އb)ʩt zlC\TsM{W"Kqe9mSrr7-,Dk5dyM,W"rGey'T_5y|c!"?g>%YH jC&6 f0rJY7?\Ěuκ/=u,씲jۼ=Kw]L. lh a8;]_b}!SܝSq5;#yC9Md9~{VDcLfYbL[ؗoc mrs Ws@6c e(T+9An7@]"?ǻrc̹yDЖ2or&lǹ߰m[b4nyki?rctpB=EC)rZ2{"rKucTFBs%s|_D7ׁ^fN;o:Xb=i C0cؖOkdpVJT4~ 2Sggs<A-}t?wSdo9-7JFaM,֕ssfylƢʜ֮];Ifm芷GXjZ8)Ggz6d%{l|w}"/iȮ> }J9?dOͤ*?g=k9&x2 > n.3s%2 wuʞ9ZE9~ʹrFG:=9NM.]Zr5^?㟞Wf>wT)8S.m_\YϔHM3h&m:U*%> tf/\pL\a&N}$RI,n؎ oh޳WR@=07U9RNo]`_jEyfJK{Ok)we݃, ]SQi}5&=!?ZWi ݸV!mWԿ#dP=Mۺ:|C'/NUYQƘ]in_.#Rn[3Urtm˶ĀUldt6igud%E!wPXr,3͝-X:ӚƥZr\t'!<3&iyE;k|c[gi?찦Ρ90; 1U1ۮ9,i+x}SQʲ (툁%N= 9)bE־4>p& EN{7oQo}fBMWJ\}65 N֟a_SY/Ja}9}*Y|G'얓 ~/ztĥ1/vF+tD;C~ k#/ԯFaXHdx{3hQ8y!$b >rhňȜ)Z'=ྃW=vFHG"%~]ʙ)]*,8GLYy{Jja$>*qdμ䒱oYמ-aU,!=aJ3o xh>Q#sr/3SF;x6}.#E%̙ʈ-csE\G!>c:p$G#)RqH3^%.㲀$꤂qGLW{)ۈarE2oFȮlvaMuheR0n 3un徚`roeipw}]WM̒ p a؅L;H`GPr ޜmo~a\JGZlB&q\jef| H?$xT'abug%]m1F#v$?oP`0 x;~m&lu>b,E>)=L8?Ė*ѵD꧖gv:}w_w[: }J0d-}`Wܿghnwׅ5t(r5=}mtTʻ^^NW)PUS/ݹ:uFSSړicT=fVā'cL {97[Gxonr^Š^[ɪU \αسX(L@̐XCBrV!TdyְG|E&U SB "VkɈ@;XIo9LwkȦGݘ醴R`0 +:[(RraOxS#<",8F,(^?p867}v>q@ Vt-hNn {[~S0lq4mp #/71 P^y="IL^aa%* |<)b/nzQ<1#'o]v&{𨨽~Ӳ`mGuלWSkකn"~.kE(a}e{wQu4כNhc 60u`0 dܣzXtҕAr!DcVȃ::{1і^.NFݠᩚ0Ø: :IЩvxGA&ǧ_r'KɱVfKi^ɉ$l{ձdiZ^[эnէG+[^ŽڃZ3Y|I=%10r8 #ȌlM} l:j[Y^Ƈ(Sac+s(U0 `0 `0 `0 `0 gunicorn-17.5/docs/site/index.html000066400000000000000000000174141216475616600172100ustar00rootroot00000000000000 Gunicorn - Python WSGI HTTP Server for UNIX
Latest version: 0.17.4

Installation

Here's a quick rundown on how to get started with gunicorn. For more detail read the documentation.

  $ sudo pip install virtualenv
  $ mkdir ~/environments/
  $ virtualenv ~/environments/tutorial/
  $ cd ~/environments/tutorial/
  $ ls
  bin  include  lib
  $ source bin/activate
  (tutorial) $ pip install gunicorn
  (tutorial) $ mkdir myapp
  (tutorial) $ cd myapp/
  (tutorial) $ vi myapp.py
  (tutorial) $ cat myapp.py

  def app(environ, start_response):
      data = "Hello, World!\n"
      start_response("200 OK", [
          ("Content-Type", "text/plain"),
          ("Content-Length", str(len(data)))
      ])
      return iter([data])

  (tutorial) $ ../bin/gunicorn -w 4 myapp:app
  2010-06-05 23:27:07 [16800] [INFO] Arbiter booted
  2010-06-05 23:27:07 [16800] [INFO] Listening at: http://127.0.0.1:8000
  2010-06-05 23:27:07 [16801] [INFO] Worker spawned (pid: 16801)
  2010-06-05 23:27:07 [16802] [INFO] Worker spawned (pid: 16802)
  2010-06-05 23:27:07 [16803] [INFO] Worker spawned (pid: 16803)
  2010-06-05 23:27:07 [16804] [INFO] Worker spawned (pid: 16804)

Deployment

Gunicorn is wsgi http server. It is best to use Gunicorn behind HTTP proxy server. We strongly advise you to use nginx.

Here's an example to help you get started with using nginx.

  server {
    listen 80;
    server_name example.org;
    access_log  /var/log/nginx/example.log;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
  }

Nginx is set up as reverse proxy server to gunicorn server which is running at localhost port 8000.

Read the full documentation at docs.gunicorn.org

Mailing list

The user mailing list is general discussion and support list for Gunicorn users.

The archive for this list can also be browsed online.

Irc

The Gunicorn channel is on the Freenode IRC network. You can chat with other on #gunicorn channel.

Issue Tracking

Bug reports, enhancement requests and tasks generally go in the Github issue tracker.

Documentation

You can read more comprehensive documentation at docs.gunicorn.org.

The contents are:

gunicorn-17.5/docs/site/install.html000066400000000000000000000004521216475616600175410ustar00rootroot00000000000000 Green Unicorn - Install

Redirecting to here

gunicorn-17.5/docs/site/installation.html000066400000000000000000000004521216475616600205740ustar00rootroot00000000000000 Green Unicorn - Install

Redirecting to here

gunicorn-17.5/docs/site/js/000077500000000000000000000000001216475616600156205ustar00rootroot00000000000000gunicorn-17.5/docs/site/js/main.js000077500000000000000000000027071216475616600171130ustar00rootroot00000000000000$(document).ready(function() { Tabs.init(); }); var Tabs = { init: function(){ var activateTab = function ($tab) { var // this links tabs set $tabs = $tab.parents('.tabs'), // currently active tab activeTab = { 'tab' : $tabs.find('ul').children('li.active'), 'content' : $tabs.find('div[data-tab].active') }, // newly clicked tab newTab = { 'tab' : $tab.parent('li'), 'content' : $tabs.find('[data-tab=' + $tab.attr('href').replace('#', '') + ']') }, x, y; // remove active class from tab and content for (x in activeTab) { activeTab[x].removeClass('active'); } // add active class to tab and content for (y in newTab) { newTab[y].addClass('active'); } }; // hook up tab links $(document).on('click', '.tabs ul li a', function(e) { activateTab($(this)); //alert($(this)); }); // hook up initial load active tab if (window.location.hash) { var $activeTab = $('a[href="' + window.location.hash + '"]'); if ($activeTab.length && $activeTab.parents('.tabs').length) { activateTab($activeTab); } } } };gunicorn-17.5/docs/site/news.html000066400000000000000000000004471216475616600170530ustar00rootroot00000000000000 Green Unicorn - News

Redirecting to here

gunicorn-17.5/docs/site/run.html000066400000000000000000000004461216475616600167020ustar00rootroot00000000000000 Green Unicorn - Run

Redirecting to here

gunicorn-17.5/docs/site/sitemap.xml000066400000000000000000000060351216475616600173740ustar00rootroot00000000000000 http://gunicorn.org/ 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/configuration.html 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/configure.html 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/css/ 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/css/index.css 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/css/style.css 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/deploy.html 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/deployment.html 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/design.html 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/faq.html 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/images/ 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/images/gunicorn.png 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/images/large_gunicorn.png 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/images/logo.png 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/index.html 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/install.html 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/installation.html 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/news.html 2010-07-08T19:57:19Z 0.5000 http://gunicorn.org/run.html 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/tuning.html 2010-07-01T05:14:22Z 0.5000 http://gunicorn.org/usage.html 2010-07-01T05:14:22Z 0.5000 gunicorn-17.5/docs/site/tuning.html000066400000000000000000000004461216475616600174020ustar00rootroot00000000000000 Green Unicorn - FAQ

Redirecting to here

gunicorn-17.5/docs/site/usage.html000066400000000000000000000004461216475616600172020ustar00rootroot00000000000000 Green Unicorn - Run

Redirecting to here

gunicorn-17.5/docs/sitemap_config.xml000066400000000000000000000010571216475616600177540ustar00rootroot00000000000000 gunicorn-17.5/docs/sitemap_gen.py000077500000000000000000002123521216475616600171150ustar00rootroot00000000000000#!/usr/bin/env python # # Copyright (c) 2004, 2005 Google Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of Google nor the names of its contributors may # be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # # The sitemap_gen.py script is written in Python 2.2 and released to # the open source community for continuous improvements under the BSD # 2.0 new license, which can be found at: # # http://www.opensource.org/licenses/bsd-license.php # __usage__ = \ """A simple script to automatically produce sitemaps for a webserver, in the Google Sitemap Protocol (GSP). Usage: python sitemap_gen.py --config=config.xml [--help] [--testing] --config=config.xml, specifies config file location --help, displays usage message --testing, specified when user is experimenting """ # Please be careful that all syntax used in this file can be parsed on # Python 1.5 -- this version check is not evaluated until after the # entire file has been parsed. import sys if sys.hexversion < 0x02020000: print 'This script requires Python 2.2 or later.' print 'Currently run with version: %s' % sys.version sys.exit(1) import fnmatch import glob import gzip import hashlib import os import re import stat import time import types import urllib import urlparse import xml.sax # True and False were introduced in Python2.2.2 try: testTrue=True del testTrue except NameError: True=1 False=0 # Text encodings ENC_ASCII = 'ASCII' ENC_UTF8 = 'UTF-8' ENC_IDNA = 'IDNA' ENC_ASCII_LIST = ['ASCII', 'US-ASCII', 'US', 'IBM367', 'CP367', 'ISO646-US' 'ISO_646.IRV:1991', 'ISO-IR-6', 'ANSI_X3.4-1968', 'ANSI_X3.4-1986', 'CPASCII' ] ENC_DEFAULT_LIST = ['ISO-8859-1', 'ISO-8859-2', 'ISO-8859-5'] # Maximum number of urls in each sitemap, before next Sitemap is created MAXURLS_PER_SITEMAP = 50000 # Suffix on a Sitemap index file SITEINDEX_SUFFIX = '_index.xml' # Regular expressions tried for extracting URLs from access logs. ACCESSLOG_CLF_PATTERN = re.compile( r'.+\s+"([^\s]+)\s+([^\s]+)\s+HTTP/\d+\.\d+"\s+200\s+.*' ) # Match patterns for lastmod attributes LASTMOD_PATTERNS = map(re.compile, [ r'^\d\d\d\d$', r'^\d\d\d\d-\d\d$', r'^\d\d\d\d-\d\d-\d\d$', r'^\d\d\d\d-\d\d-\d\dT\d\d:\d\dZ$', r'^\d\d\d\d-\d\d-\d\dT\d\d:\d\d[+-]\d\d:\d\d$', r'^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?Z$', r'^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?[+-]\d\d:\d\d$', ]) # Match patterns for changefreq attributes CHANGEFREQ_PATTERNS = [ 'always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never' ] # XML formats SITEINDEX_HEADER = \ '\n' \ '\n' SITEINDEX_FOOTER = '\n' SITEINDEX_ENTRY = \ ' \n' \ ' %(loc)s\n' \ ' %(lastmod)s\n' \ ' \n' SITEMAP_HEADER = \ '\n' \ '\n' SITEMAP_FOOTER = '\n' SITEURL_XML_PREFIX = ' \n' SITEURL_XML_SUFFIX = ' \n' # Search engines to notify with the updated sitemaps # # This list is very non-obvious in what's going on. Here's the gist: # Each item in the list is a 6-tuple of items. The first 5 are "almost" # the same as the input arguments to urlparse.urlunsplit(): # 0 - schema # 1 - netloc # 2 - path # 3 - query <-- EXCEPTION: specify a query map rather than a string # 4 - fragment # Additionally, add item 5: # 5 - query attribute that should be set to the new Sitemap URL # Clear as mud, I know. NOTIFICATION_SITES = [ ('http', 'www.google.com', 'webmasters/sitemaps/ping', {}, '', 'sitemap') ] class Error(Exception): """ Base exception class. In this module we tend not to use our own exception types for very much, but they come in very handy on XML parsing with SAX. """ pass #end class Error class SchemaError(Error): """Failure to process an XML file according to the schema we know.""" pass #end class SchemeError class Encoder: """ Manages wide-character/narrow-character conversions for just about all text that flows into or out of the script. You should always use this class for string coercion, as opposed to letting Python handle coercions automatically. Reason: Python usually assumes ASCII (7-bit) as a default narrow character encoding, which is not the kind of data we generally deal with. General high-level methodologies used in sitemap_gen: [PATHS] File system paths may be wide or narrow, depending on platform. This works fine, just be aware of it and be very careful to not mix them. That is, if you have to pass several file path arguments into a library call, make sure they are all narrow or all wide. This class has MaybeNarrowPath() which should be called on every file system path you deal with. [URLS] URL locations are stored in Narrow form, already escaped. This has the benefit of keeping escaping and encoding as close as possible to the format we read them in. The downside is we may end up with URLs that have intermingled encodings -- the root path may be encoded in one way while the filename is encoded in another. This is obviously wrong, but it should hopefully be an issue hit by very few users. The workaround from the user level (assuming they notice) is to specify a default_encoding parameter in their config file. [OTHER] Other text, such as attributes of the URL class, configuration options, etc, are generally stored in Unicode for simplicity. """ def __init__(self): self._user = None # User-specified default encoding self._learned = [] # Learned default encodings self._widefiles = False # File system can be wide # Can the file system be Unicode? try: self._widefiles = os.path.supports_unicode_filenames except AttributeError: try: self._widefiles = sys.getwindowsversion() == os.VER_PLATFORM_WIN32_NT except AttributeError: pass # Try to guess a working default try: encoding = sys.getfilesystemencoding() if encoding and not (encoding.upper() in ENC_ASCII_LIST): self._learned = [ encoding ] except AttributeError: pass if not self._learned: encoding = sys.getdefaultencoding() if encoding and not (encoding.upper() in ENC_ASCII_LIST): self._learned = [ encoding ] # If we had no guesses, start with some European defaults if not self._learned: self._learned = ENC_DEFAULT_LIST #end def __init__ def SetUserEncoding(self, encoding): self._user = encoding #end def SetUserEncoding def NarrowText(self, text, encoding): """ Narrow a piece of arbitrary text """ if type(text) != types.UnicodeType: return text # Try the passed in preference if encoding: try: result = text.encode(encoding) if not encoding in self._learned: self._learned.append(encoding) return result except UnicodeError: pass except LookupError: output.Warn('Unknown encoding: %s' % encoding) # Try the user preference if self._user: try: return text.encode(self._user) except UnicodeError: pass except LookupError: temp = self._user self._user = None output.Warn('Unknown default_encoding: %s' % temp) # Look through learned defaults, knock any failing ones out of the list while self._learned: try: return text.encode(self._learned[0]) except: del self._learned[0] # When all other defaults are exhausted, use UTF-8 try: return text.encode(ENC_UTF8) except UnicodeError: pass # Something is seriously wrong if we get to here return text.encode(ENC_ASCII, 'ignore') #end def NarrowText def MaybeNarrowPath(self, text): """ Paths may be allowed to stay wide """ if self._widefiles: return text return self.NarrowText(text, None) #end def MaybeNarrowPath def WidenText(self, text, encoding): """ Widen a piece of arbitrary text """ if type(text) != types.StringType: return text # Try the passed in preference if encoding: try: result = unicode(text, encoding) if not encoding in self._learned: self._learned.append(encoding) return result except UnicodeError: pass except LookupError: output.Warn('Unknown encoding: %s' % encoding) # Try the user preference if self._user: try: return unicode(text, self._user) except UnicodeError: pass except LookupError: temp = self._user self._user = None output.Warn('Unknown default_encoding: %s' % temp) # Look through learned defaults, knock any failing ones out of the list while self._learned: try: return unicode(text, self._learned[0]) except: del self._learned[0] # When all other defaults are exhausted, use UTF-8 try: return unicode(text, ENC_UTF8) except UnicodeError: pass # Getting here means it wasn't UTF-8 and we had no working default. # We really don't have anything "right" we can do anymore. output.Warn('Unrecognized encoding in text: %s' % text) if not self._user: output.Warn('You may need to set a default_encoding in your ' 'configuration file.') return text.decode(ENC_ASCII, 'ignore') #end def WidenText #end class Encoder encoder = Encoder() class Output: """ Exposes logging functionality, and tracks how many errors we have thus output. Logging levels should be used as thus: Fatal -- extremely sparingly Error -- config errors, entire blocks of user 'intention' lost Warn -- individual URLs lost Log(,0) -- Un-suppressable text that's not an error Log(,1) -- touched files, major actions Log(,2) -- parsing notes, filtered or duplicated URLs Log(,3) -- each accepted URL """ def __init__(self): self.num_errors = 0 # Count of errors self.num_warns = 0 # Count of warnings self._errors_shown = {} # Shown errors self._warns_shown = {} # Shown warnings self._verbose = 0 # Level of verbosity #end def __init__ def Log(self, text, level): """ Output a blurb of diagnostic text, if the verbose level allows it """ if text: text = encoder.NarrowText(text, None) if self._verbose >= level: print text #end def Log def Warn(self, text): """ Output and count a warning. Suppress duplicate warnings. """ if text: text = encoder.NarrowText(text, None) hash = hashlib.md5(text).hexdigest() if not self._warns_shown.has_key(hash): self._warns_shown[hash] = 1 print '[WARNING] ' + text else: self.Log('(suppressed) [WARNING] ' + text, 3) self.num_warns = self.num_warns + 1 #end def Warn def Error(self, text): """ Output and count an error. Suppress duplicate errors. """ if text: text = encoder.NarrowText(text, None) hash = hashlib.md5(text).hexdigest() if not self._errors_shown.has_key(hash): self._errors_shown[hash] = 1 print '[ERROR] ' + text else: self.Log('(suppressed) [ERROR] ' + text, 3) self.num_errors = self.num_errors + 1 #end def Error def Fatal(self, text): """ Output an error and terminate the program. """ if text: text = encoder.NarrowText(text, None) print '[FATAL] ' + text else: print 'Fatal error.' sys.exit(1) #end def Fatal def SetVerbose(self, level): """ Sets the verbose level. """ try: if type(level) != types.IntType: level = int(level) if (level >= 0) and (level <= 3): self._verbose = level return except ValueError: pass self.Error('Verbose level (%s) must be between 0 and 3 inclusive.' % level) #end def SetVerbose #end class Output output = Output() class URL(object): """ URL is a smart structure grouping together the properties we care about for a single web reference. """ __slots__ = 'loc', 'lastmod', 'changefreq', 'priority' def __init__(self): self.loc = None # URL -- in Narrow characters self.lastmod = None # ISO8601 timestamp of last modify self.changefreq = None # Text term for update frequency self.priority = None # Float between 0 and 1 (inc) #end def __init__ def __cmp__(self, other): if self.loc < other.loc: return -1 if self.loc > other.loc: return 1 return 0 #end def __cmp__ def TrySetAttribute(self, attribute, value): """ Attempt to set the attribute to the value, with a pretty try block around it. """ if attribute == 'loc': self.loc = self.Canonicalize(value) else: try: setattr(self, attribute, value) except AttributeError: output.Warn('Unknown URL attribute: %s' % attribute) #end def TrySetAttribute def IsAbsolute(loc): """ Decide if the URL is absolute or not """ if not loc: return False narrow = encoder.NarrowText(loc, None) (scheme, netloc, path, query, frag) = urlparse.urlsplit(narrow) if (not scheme) or (not netloc): return False return True #end def IsAbsolute IsAbsolute = staticmethod(IsAbsolute) def Canonicalize(loc): """ Do encoding and canonicalization on a URL string """ if not loc: return loc # Let the encoder try to narrow it narrow = encoder.NarrowText(loc, None) # Escape components individually (scheme, netloc, path, query, frag) = urlparse.urlsplit(narrow) unr = '-._~' sub = '!$&\'()*+,;=' netloc = urllib.quote(netloc, unr + sub + '%:@/[]') path = urllib.quote(path, unr + sub + '%:@/') query = urllib.quote(query, unr + sub + '%:@/?') frag = urllib.quote(frag, unr + sub + '%:@/?') # Try built-in IDNA encoding on the netloc try: (ignore, widenetloc, ignore, ignore, ignore) = urlparse.urlsplit(loc) for c in widenetloc: if c >= unichr(128): netloc = widenetloc.encode(ENC_IDNA) netloc = urllib.quote(netloc, unr + sub + '%:@/[]') break except UnicodeError: # urlsplit must have failed, based on implementation differences in the # library. There is not much we can do here, except ignore it. pass except LookupError: output.Warn('An International Domain Name (IDN) is being used, but this ' 'version of Python does not have support for IDNA encoding. ' ' (IDNA support was introduced in Python 2.3) The encoding ' 'we have used instead is wrong and will probably not yield ' 'valid URLs.') bad_netloc = False if '%' in netloc: bad_netloc = True # Put it all back together narrow = urlparse.urlunsplit((scheme, netloc, path, query, frag)) # I let '%' through. Fix any that aren't pre-existing escapes. HEXDIG = '0123456789abcdefABCDEF' list = narrow.split('%') narrow = list[0] del list[0] for item in list: if (len(item) >= 2) and (item[0] in HEXDIG) and (item[1] in HEXDIG): narrow = narrow + '%' + item else: narrow = narrow + '%25' + item # Issue a warning if this is a bad URL if bad_netloc: output.Warn('Invalid characters in the host or domain portion of a URL: ' + narrow) return narrow #end def Canonicalize Canonicalize = staticmethod(Canonicalize) def Validate(self, base_url, allow_fragment): """ Verify the data in this URL is well-formed, and override if not. """ assert type(base_url) == types.StringType # Test (and normalize) the ref if not self.loc: output.Warn('Empty URL') return False if allow_fragment: self.loc = urlparse.urljoin(base_url, self.loc) if not self.loc.startswith(base_url): output.Warn('Discarded URL for not starting with the base_url: %s' % self.loc) self.loc = None return False # Test the lastmod if self.lastmod: match = False self.lastmod = self.lastmod.upper() for pattern in LASTMOD_PATTERNS: match = pattern.match(self.lastmod) if match: break if not match: output.Warn('Lastmod "%s" does not appear to be in ISO8601 format on ' 'URL: %s' % (self.lastmod, self.loc)) self.lastmod = None # Test the changefreq if self.changefreq: match = False self.changefreq = self.changefreq.lower() for pattern in CHANGEFREQ_PATTERNS: if self.changefreq == pattern: match = True break if not match: output.Warn('Changefreq "%s" is not a valid change frequency on URL ' ': %s' % (self.changefreq, self.loc)) self.changefreq = None # Test the priority if self.priority: priority = -1.0 try: priority = float(self.priority) except ValueError: pass if (priority < 0.0) or (priority > 1.0): output.Warn('Priority "%s" is not a number between 0 and 1 inclusive ' 'on URL: %s' % (self.priority, self.loc)) self.priority = None return True #end def Validate def MakeHash(self): """ Provides a uniform way of hashing URLs """ if not self.loc: return None if self.loc.endswith('/'): return hashlib.md5(self.loc[:-1]).hexdigest() return hashlib.md5(self.loc).hexdigest() #end def MakeHash def Log(self, prefix='URL', level=3): """ Dump the contents, empty or not, to the log. """ out = prefix + ':' for attribute in self.__slots__: value = getattr(self, attribute) if not value: value = '' out = out + (' %s=[%s]' % (attribute, value)) output.Log('%s' % encoder.NarrowText(out, None), level) #end def Log def WriteXML(self, file): """ Dump non-empty contents to the output file, in XML format. """ if not self.loc: return out = SITEURL_XML_PREFIX for attribute in self.__slots__: value = getattr(self, attribute) if value: if type(value) == types.UnicodeType: value = encoder.NarrowText(value, None) elif type(value) != types.StringType: value = str(value) value = xml.sax.saxutils.escape(value) out = out + (' <%s>%s\n' % (attribute, value, attribute)) out = out + SITEURL_XML_SUFFIX file.write(out) #end def WriteXML #end class URL class Filter: """ A filter on the stream of URLs we find. A filter is, in essence, a wildcard applied to the stream. You can think of this as an operator that returns a tri-state when given a URL: True -- this URL is to be included in the sitemap None -- this URL is undecided False -- this URL is to be dropped from the sitemap """ def __init__(self, attributes): self._wildcard = None # Pattern for wildcard match self._regexp = None # Pattern for regexp match self._pass = False # "Drop" filter vs. "Pass" filter if not ValidateAttributes('FILTER', attributes, ('pattern', 'type', 'action')): return # Check error count on the way in num_errors = output.num_errors # Fetch the attributes pattern = attributes.get('pattern') type = attributes.get('type', 'wildcard') action = attributes.get('action', 'drop') if type: type = type.lower() if action: action = action.lower() # Verify the attributes if not pattern: output.Error('On a filter you must specify a "pattern" to match') elif (not type) or ((type != 'wildcard') and (type != 'regexp')): output.Error('On a filter you must specify either \'type="wildcard"\' ' 'or \'type="regexp"\'') elif (action != 'pass') and (action != 'drop'): output.Error('If you specify a filter action, it must be either ' '\'action="pass"\' or \'action="drop"\'') # Set the rule if action == 'drop': self._pass = False elif action == 'pass': self._pass = True if type == 'wildcard': self._wildcard = pattern elif type == 'regexp': try: self._regexp = re.compile(pattern) except re.error: output.Error('Bad regular expression: %s' % pattern) # Log the final results iff we didn't add any errors if num_errors == output.num_errors: output.Log('Filter: %s any URL that matches %s "%s"' % (action, type, pattern), 2) #end def __init__ def Apply(self, url): """ Process the URL, as above. """ if (not url) or (not url.loc): return None if self._wildcard: if fnmatch.fnmatchcase(url.loc, self._wildcard): return self._pass return None if self._regexp: if self._regexp.search(url.loc): return self._pass return None assert False # unreachable #end def Apply #end class Filter class InputURL: """ Each Input class knows how to yield a set of URLs from a data source. This one handles a single URL, manually specified in the config file. """ def __init__(self, attributes): self._url = None # The lonely URL if not ValidateAttributes('URL', attributes, ('href', 'lastmod', 'changefreq', 'priority')): return url = URL() for attr in attributes.keys(): if attr == 'href': url.TrySetAttribute('loc', attributes[attr]) else: url.TrySetAttribute(attr, attributes[attr]) if not url.loc: output.Error('Url entries must have an href attribute.') return self._url = url output.Log('Input: From URL "%s"' % self._url.loc, 2) #end def __init__ def ProduceURLs(self, consumer): """ Produces URLs from our data source, hands them in to the consumer. """ if self._url: consumer(self._url, True) #end def ProduceURLs #end class InputURL class InputURLList: """ Each Input class knows how to yield a set of URLs from a data source. This one handles a text file with a list of URLs """ def __init__(self, attributes): self._path = None # The file path self._encoding = None # Encoding of that file if not ValidateAttributes('URLLIST', attributes, ('path', 'encoding')): return self._path = attributes.get('path') self._encoding = attributes.get('encoding', ENC_UTF8) if self._path: self._path = encoder.MaybeNarrowPath(self._path) if os.path.isfile(self._path): output.Log('Input: From URLLIST "%s"' % self._path, 2) else: output.Error('Can not locate file: %s' % self._path) self._path = None else: output.Error('Urllist entries must have a "path" attribute.') #end def __init__ def ProduceURLs(self, consumer): """ Produces URLs from our data source, hands them in to the consumer. """ # Open the file (frame, file) = OpenFileForRead(self._path, 'URLLIST') if not file: return # Iterate lines linenum = 0 for line in file.readlines(): linenum = linenum + 1 # Strip comments and empty lines if self._encoding: line = encoder.WidenText(line, self._encoding) line = line.strip() if (not line) or line[0] == '#': continue # Split the line on space url = URL() cols = line.split(' ') for i in range(0,len(cols)): cols[i] = cols[i].strip() url.TrySetAttribute('loc', cols[0]) # Extract attributes from the other columns for i in range(1,len(cols)): if cols[i]: try: (attr_name, attr_val) = cols[i].split('=', 1) url.TrySetAttribute(attr_name, attr_val) except ValueError: output.Warn('Line %d: Unable to parse attribute: %s' % (linenum, cols[i])) # Pass it on consumer(url, False) file.close() if frame: frame.close() #end def ProduceURLs #end class InputURLList class InputDirectory: """ Each Input class knows how to yield a set of URLs from a data source. This one handles a directory that acts as base for walking the filesystem. """ def __init__(self, attributes, base_url): self._path = None # The directory self._url = None # The URL equivelant self._default_file = None if not ValidateAttributes('DIRECTORY', attributes, ('path', 'url', 'default_file')): return # Prep the path -- it MUST end in a sep path = attributes.get('path') if not path: output.Error('Directory entries must have both "path" and "url" ' 'attributes') return path = encoder.MaybeNarrowPath(path) if not path.endswith(os.sep): path = path + os.sep if not os.path.isdir(path): output.Error('Can not locate directory: %s' % path) return # Prep the URL -- it MUST end in a sep url = attributes.get('url') if not url: output.Error('Directory entries must have both "path" and "url" ' 'attributes') return url = URL.Canonicalize(url) if not url.endswith('/'): url = url + '/' if not url.startswith(base_url): url = urlparse.urljoin(base_url, url) if not url.startswith(base_url): output.Error('The directory URL "%s" is not relative to the ' 'base_url: %s' % (url, base_url)) return # Prep the default file -- it MUST be just a filename file = attributes.get('default_file') if file: file = encoder.MaybeNarrowPath(file) if os.sep in file: output.Error('The default_file "%s" can not include path information.' % file) file = None self._path = path self._url = url self._default_file = file if file: output.Log('Input: From DIRECTORY "%s" (%s) with default file "%s"' % (path, url, file), 2) else: output.Log('Input: From DIRECTORY "%s" (%s) with no default file' % (path, url), 2) #end def __init__ def ProduceURLs(self, consumer): """ Produces URLs from our data source, hands them in to the consumer. """ if not self._path: return root_path = self._path root_URL = self._url root_file = self._default_file def PerFile(dirpath, name): """ Called once per file. Note that 'name' will occasionally be None -- for a directory itself """ # Pull a timestamp url = URL() isdir = False try: if name: path = os.path.join(dirpath, name) else: path = dirpath isdir = os.path.isdir(path) time = None if isdir and root_file: file = os.path.join(path, root_file) try: time = os.stat(file)[stat.ST_MTIME]; except OSError: pass if not time: time = os.stat(path)[stat.ST_MTIME]; url.lastmod = TimestampISO8601(time) except OSError: pass except ValueError: pass # Build a URL middle = dirpath[len(root_path):] if os.sep != '/': middle = middle.replace(os.sep, '/') if middle: middle = middle + '/' if name: middle = middle + name if isdir: middle = middle + '/' url.TrySetAttribute('loc', root_URL + encoder.WidenText(middle, None)) # Suppress default files. (All the way down here so we can log it.) if name and (root_file == name): url.Log(prefix='IGNORED (default file)', level=2) return consumer(url, False) #end def PerFile def PerDirectory(ignore, dirpath, namelist): """ Called once per directory with a list of all the contained files/dirs. """ ignore = ignore # Avoid warnings of an unused parameter if not dirpath.startswith(root_path): output.Warn('Unable to decide what the root path is for directory: ' '%s' % dirpath) return for name in namelist: PerFile(dirpath, name) #end def PerDirectory output.Log('Walking DIRECTORY "%s"' % self._path, 1) PerFile(self._path, None) os.path.walk(self._path, PerDirectory, None) #end def ProduceURLs #end class InputDirectory class InputAccessLog: """ Each Input class knows how to yield a set of URLs from a data source. This one handles access logs. It's non-trivial in that we want to auto-detect log files in the Common Logfile Format (as used by Apache, for instance) and the Extended Log File Format (as used by IIS, for instance). """ def __init__(self, attributes): self._path = None # The file path self._encoding = None # Encoding of that file self._is_elf = False # Extended Log File Format? self._is_clf = False # Common Logfile Format? self._elf_status = -1 # ELF field: '200' self._elf_method = -1 # ELF field: 'HEAD' self._elf_uri = -1 # ELF field: '/foo?bar=1' self._elf_urifrag1 = -1 # ELF field: '/foo' self._elf_urifrag2 = -1 # ELF field: 'bar=1' if not ValidateAttributes('ACCESSLOG', attributes, ('path', 'encoding')): return self._path = attributes.get('path') self._encoding = attributes.get('encoding', ENC_UTF8) if self._path: self._path = encoder.MaybeNarrowPath(self._path) if os.path.isfile(self._path): output.Log('Input: From ACCESSLOG "%s"' % self._path, 2) else: output.Error('Can not locate file: %s' % self._path) self._path = None else: output.Error('Accesslog entries must have a "path" attribute.') #end def __init__ def RecognizeELFLine(self, line): """ Recognize the Fields directive that heads an ELF file """ if not line.startswith('#Fields:'): return False fields = line.split(' ') del fields[0] for i in range(0, len(fields)): field = fields[i].strip() if field == 'sc-status': self._elf_status = i elif field == 'cs-method': self._elf_method = i elif field == 'cs-uri': self._elf_uri = i elif field == 'cs-uri-stem': self._elf_urifrag1 = i elif field == 'cs-uri-query': self._elf_urifrag2 = i output.Log('Recognized an Extended Log File Format file.', 2) return True #end def RecognizeELFLine def GetELFLine(self, line): """ Fetch the requested URL from an ELF line """ fields = line.split(' ') count = len(fields) # Verify status was Ok if self._elf_status >= 0: if self._elf_status >= count: return None if not fields[self._elf_status].strip() == '200': return None # Verify method was HEAD or GET if self._elf_method >= 0: if self._elf_method >= count: return None if not fields[self._elf_method].strip() in ('HEAD', 'GET'): return None # Pull the full URL if we can if self._elf_uri >= 0: if self._elf_uri >= count: return None url = fields[self._elf_uri].strip() if url != '-': return url # Put together a fragmentary URL if self._elf_urifrag1 >= 0: if self._elf_urifrag1 >= count or self._elf_urifrag2 >= count: return None urlfrag1 = fields[self._elf_urifrag1].strip() urlfrag2 = None if self._elf_urifrag2 >= 0: urlfrag2 = fields[self._elf_urifrag2] if urlfrag1 and (urlfrag1 != '-'): if urlfrag2 and (urlfrag2 != '-'): urlfrag1 = urlfrag1 + '?' + urlfrag2 return urlfrag1 return None #end def GetELFLine def RecognizeCLFLine(self, line): """ Try to tokenize a logfile line according to CLF pattern and see if it works. """ match = ACCESSLOG_CLF_PATTERN.match(line) recognize = match and (match.group(1) in ('HEAD', 'GET')) if recognize: output.Log('Recognized a Common Logfile Format file.', 2) return recognize #end def RecognizeCLFLine def GetCLFLine(self, line): """ Fetch the requested URL from a CLF line """ match = ACCESSLOG_CLF_PATTERN.match(line) if match: request = match.group(1) if request in ('HEAD', 'GET'): return match.group(2) return None #end def GetCLFLine def ProduceURLs(self, consumer): """ Produces URLs from our data source, hands them in to the consumer. """ # Open the file (frame, file) = OpenFileForRead(self._path, 'ACCESSLOG') if not file: return # Iterate lines for line in file.readlines(): if self._encoding: line = encoder.WidenText(line, self._encoding) line = line.strip() # If we don't know the format yet, try them both if (not self._is_clf) and (not self._is_elf): self._is_elf = self.RecognizeELFLine(line) self._is_clf = self.RecognizeCLFLine(line) # Digest the line match = None if self._is_elf: match = self.GetELFLine(line) elif self._is_clf: match = self.GetCLFLine(line) if not match: continue # Pass it on url = URL() url.TrySetAttribute('loc', match) consumer(url, True) file.close() if frame: frame.close() #end def ProduceURLs #end class InputAccessLog class InputSitemap(xml.sax.handler.ContentHandler): """ Each Input class knows how to yield a set of URLs from a data source. This one handles Sitemap files and Sitemap index files. For the sake of simplicity in design (and simplicity in interfacing with the SAX package), we do not handle these at the same time, recursively. Instead we read an index file completely and make a list of Sitemap files, then go back and process each Sitemap. """ class _ContextBase(object): """Base class for context handlers in our SAX processing. A context handler is a class that is responsible for understanding one level of depth in the XML schema. The class knows what sub-tags are allowed, and doing any processing specific for the tag we're in. This base class is the API filled in by specific context handlers, all defined below. """ def __init__(self, subtags): """Initialize with a sequence of the sub-tags that would be valid in this context.""" self._allowed_tags = subtags # Sequence of sub-tags we can have self._last_tag = None # Most recent seen sub-tag #end def __init__ def AcceptTag(self, tag): """Returns True iff opening a sub-tag is valid in this context.""" valid = tag in self._allowed_tags if valid: self._last_tag = tag else: self._last_tag = None return valid #end def AcceptTag def AcceptText(self, text): """Returns True iff a blurb of text is valid in this context.""" return False #end def AcceptText def Open(self): """The context is opening. Do initialization.""" pass #end def Open def Close(self): """The context is closing. Return our result, if any.""" pass #end def Close def Return(self, result): """We're returning to this context after handling a sub-tag. This method is called with the result data from the sub-tag that just closed. Here in _ContextBase, if we ever see a result it means the derived child class forgot to override this method.""" if result: raise NotImplementedError #end def Return #end class _ContextBase class _ContextUrlSet(_ContextBase): """Context handler for the document node in a Sitemap.""" def __init__(self): InputSitemap._ContextBase.__init__(self, ('url',)) #end def __init__ #end class _ContextUrlSet class _ContextUrl(_ContextBase): """Context handler for a URL node in a Sitemap.""" def __init__(self, consumer): """Initialize this context handler with the callable consumer that wants our URLs.""" InputSitemap._ContextBase.__init__(self, URL.__slots__) self._url = None # The URL object we're building self._consumer = consumer # Who wants to consume it #end def __init__ def Open(self): """Initialize the URL.""" assert not self._url self._url = URL() #end def Open def Close(self): """Pass the URL to the consumer and reset it to None.""" assert self._url self._consumer(self._url, False) self._url = None #end def Close def Return(self, result): """A value context has closed, absorb the data it gave us.""" assert self._url if result: self._url.TrySetAttribute(self._last_tag, result) #end def Return #end class _ContextUrl class _ContextSitemapIndex(_ContextBase): """Context handler for the document node in an index file.""" def __init__(self): InputSitemap._ContextBase.__init__(self, ('sitemap',)) self._loclist = [] # List of accumulated Sitemap URLs #end def __init__ def Open(self): """Just a quick verify of state.""" assert not self._loclist #end def Open def Close(self): """Return our list of accumulated URLs.""" if self._loclist: temp = self._loclist self._loclist = [] return temp #end def Close def Return(self, result): """Getting a new loc URL, add it to the collection.""" if result: self._loclist.append(result) #end def Return #end class _ContextSitemapIndex class _ContextSitemap(_ContextBase): """Context handler for a Sitemap entry in an index file.""" def __init__(self): InputSitemap._ContextBase.__init__(self, ('loc', 'lastmod')) self._loc = None # The URL to the Sitemap #end def __init__ def Open(self): """Just a quick verify of state.""" assert not self._loc #end def Open def Close(self): """Return our URL to our parent.""" if self._loc: temp = self._loc self._loc = None return temp output.Warn('In the Sitemap index file, a "sitemap" entry had no "loc".') #end def Close def Return(self, result): """A value has closed. If it was a 'loc', absorb it.""" if result and (self._last_tag == 'loc'): self._loc = result #end def Return #end class _ContextSitemap class _ContextValue(_ContextBase): """Context handler for a single value. We return just the value. The higher level context has to remember what tag led into us.""" def __init__(self): InputSitemap._ContextBase.__init__(self, ()) self._text = None #end def __init__ def AcceptText(self, text): """Allow all text, adding it to our buffer.""" if self._text: self._text = self._text + text else: self._text = text return True #end def AcceptText def Open(self): """Initialize our buffer.""" self._text = None #end def Open def Close(self): """Return what's in our buffer.""" text = self._text self._text = None if text: text = text.strip() return text #end def Close #end class _ContextValue def __init__(self, attributes): """Initialize with a dictionary of attributes from our entry in the config file.""" xml.sax.handler.ContentHandler.__init__(self) self._pathlist = None # A list of files self._current = -1 # Current context in _contexts self._contexts = None # The stack of contexts we allow self._contexts_idx = None # ...contexts for index files self._contexts_stm = None # ...contexts for Sitemap files if not ValidateAttributes('SITEMAP', attributes, ['path']): return # Init the first file path path = attributes.get('path') if path: path = encoder.MaybeNarrowPath(path) if os.path.isfile(path): output.Log('Input: From SITEMAP "%s"' % path, 2) self._pathlist = [path] else: output.Error('Can not locate file "%s"' % path) else: output.Error('Sitemap entries must have a "path" attribute.') #end def __init__ def ProduceURLs(self, consumer): """In general: Produces URLs from our data source, hand them to the callable consumer. In specific: Iterate over our list of paths and delegate the actual processing to helper methods. This is a complexity no other data source needs to suffer. We are unique in that we can have files that tell us to bring in other files. Note the decision to allow an index file or not is made in this method. If we call our parser with (self._contexts == None) the parser will grab whichever context stack can handle the file. IE: index is allowed. If instead we set (self._contexts = ...) before parsing, the parser will only use the stack we specify. IE: index not allowed. """ # Set up two stacks of contexts self._contexts_idx = [InputSitemap._ContextSitemapIndex(), InputSitemap._ContextSitemap(), InputSitemap._ContextValue()] self._contexts_stm = [InputSitemap._ContextUrlSet(), InputSitemap._ContextUrl(consumer), InputSitemap._ContextValue()] # Process the first file assert self._pathlist path = self._pathlist[0] self._contexts = None # We allow an index file here self._ProcessFile(path) # Iterate over remaining files self._contexts = self._contexts_stm # No index files allowed for path in self._pathlist[1:]: self._ProcessFile(path) #end def ProduceURLs def _ProcessFile(self, path): """Do per-file reading/parsing/consuming for the file path passed in.""" assert path # Open our file (frame, file) = OpenFileForRead(path, 'SITEMAP') if not file: return # Rev up the SAX engine try: self._current = -1 xml.sax.parse(file, self) except SchemaError: output.Error('An error in file "%s" made us abort reading the Sitemap.' % path) except IOError: output.Error('Cannot read from file "%s"' % path) except xml.sax._exceptions.SAXParseException, e: output.Error('XML error in the file "%s" (line %d, column %d): %s' % (path, e._linenum, e._colnum, e.getMessage())) # Clean up file.close() if frame: frame.close() #end def _ProcessFile def _MungeLocationListIntoFiles(self, urllist): """Given a list of URLs, munge them into our self._pathlist property. We do this by assuming all the files live in the same directory as the first file in the existing pathlist. That is, we assume a Sitemap index points to Sitemaps only in the same directory. This is not true in general, but will be true for any output produced by this script. """ assert self._pathlist path = self._pathlist[0] path = os.path.normpath(path) dir = os.path.dirname(path) wide = False if type(path) == types.UnicodeType: wide = True for url in urllist: url = URL.Canonicalize(url) output.Log('Index points to Sitemap file at: %s' % url, 2) (scheme, netloc, path, query, frag) = urlparse.urlsplit(url) file = os.path.basename(path) file = urllib.unquote(file) if wide: file = encoder.WidenText(file) if dir: file = dir + os.sep + file if file: self._pathlist.append(file) output.Log('Will attempt to read Sitemap file: %s' % file, 1) #end def _MungeLocationListIntoFiles def startElement(self, tag, attributes): """SAX processing, called per node in the config stream. As long as the new tag is legal in our current context, this becomes an Open call on one context deeper. """ # If this is the document node, we may have to look for a context stack if (self._current < 0) and not self._contexts: assert self._contexts_idx and self._contexts_stm if tag == 'urlset': self._contexts = self._contexts_stm elif tag == 'sitemapindex': self._contexts = self._contexts_idx output.Log('File is a Sitemap index.', 2) else: output.Error('The document appears to be neither a Sitemap nor a ' 'Sitemap index.') raise SchemaError # Display a kinder error on a common mistake if (self._current < 0) and (self._contexts == self._contexts_stm) and ( tag == 'sitemapindex'): output.Error('A Sitemap index can not refer to another Sitemap index.') raise SchemaError # Verify no unexpected attributes if attributes: text = '' for attr in attributes.keys(): # The document node will probably have namespaces if self._current < 0: if attr.find('xmlns') >= 0: continue if attr.find('xsi') >= 0: continue if text: text = text + ', ' text = text + attr if text: output.Warn('Did not expect any attributes on any tag, instead tag ' '"%s" had attributes: %s' % (tag, text)) # Switch contexts if (self._current < 0) or (self._contexts[self._current].AcceptTag(tag)): self._current = self._current + 1 assert self._current < len(self._contexts) self._contexts[self._current].Open() else: output.Error('Can not accept tag "%s" where it appears.' % tag) raise SchemaError #end def startElement def endElement(self, tag): """SAX processing, called per node in the config stream. This becomes a call to Close on one context followed by a call to Return on the previous. """ tag = tag # Avoid warning on unused argument assert self._current >= 0 retval = self._contexts[self._current].Close() self._current = self._current - 1 if self._current >= 0: self._contexts[self._current].Return(retval) elif retval and (self._contexts == self._contexts_idx): self._MungeLocationListIntoFiles(retval) #end def endElement def characters(self, text): """SAX processing, called when text values are read. Important to note that one single text value may be split across multiple calls of this method. """ if (self._current < 0) or ( not self._contexts[self._current].AcceptText(text)): if text.strip(): output.Error('Can not accept text "%s" where it appears.' % text) raise SchemaError #end def characters #end class InputSitemap class FilePathGenerator: """ This class generates filenames in a series, upon request. You can request any iteration number at any time, you don't have to go in order. Example of iterations for '/path/foo.xml.gz': 0 --> /path/foo.xml.gz 1 --> /path/foo1.xml.gz 2 --> /path/foo2.xml.gz _index.xml --> /path/foo_index.xml """ def __init__(self): self.is_gzip = False # Is this a GZIP file? self._path = None # '/path/' self._prefix = None # 'foo' self._suffix = None # '.xml.gz' #end def __init__ def Preload(self, path): """ Splits up a path into forms ready for recombination. """ path = encoder.MaybeNarrowPath(path) # Get down to a base name path = os.path.normpath(path) base = os.path.basename(path).lower() if not base: output.Error('Couldn\'t parse the file path: %s' % path) return False lenbase = len(base) # Recognize extension lensuffix = 0 compare_suffix = ['.xml', '.xml.gz', '.gz'] for suffix in compare_suffix: if base.endswith(suffix): lensuffix = len(suffix) break if not lensuffix: output.Error('The path "%s" doesn\'t end in a supported file ' 'extension.' % path) return False self.is_gzip = suffix.endswith('.gz') # Split the original path lenpath = len(path) self._path = path[:lenpath-lenbase] self._prefix = path[lenpath-lenbase:lenpath-lensuffix] self._suffix = path[lenpath-lensuffix:] return True #end def Preload def GeneratePath(self, instance): """ Generates the iterations, as described above. """ prefix = self._path + self._prefix if type(instance) == types.IntType: if instance: return '%s%d%s' % (prefix, instance, self._suffix) return prefix + self._suffix return prefix + instance #end def GeneratePath def GenerateURL(self, instance, root_url): """ Generates iterations, but as a URL instead of a path. """ prefix = root_url + self._prefix retval = None if type(instance) == types.IntType: if instance: retval = '%s%d%s' % (prefix, instance, self._suffix) else: retval = prefix + self._suffix else: retval = prefix + instance return URL.Canonicalize(retval) #end def GenerateURL def GenerateWildURL(self, root_url): """ Generates a wildcard that should match all our iterations """ prefix = URL.Canonicalize(root_url + self._prefix) temp = URL.Canonicalize(prefix + self._suffix) suffix = temp[len(prefix):] return prefix + '*' + suffix #end def GenerateURL #end class FilePathGenerator class PerURLStatistics: """ Keep track of some simple per-URL statistics, like file extension. """ def __init__(self): self._extensions = {} # Count of extension instances #end def __init__ def Consume(self, url): """ Log some stats for the URL. At the moment, that means extension. """ if url and url.loc: (scheme, netloc, path, query, frag) = urlparse.urlsplit(url.loc) if not path: return # Recognize directories if path.endswith('/'): if self._extensions.has_key('/'): self._extensions['/'] = self._extensions['/'] + 1 else: self._extensions['/'] = 1 return # Strip to a filename i = path.rfind('/') if i >= 0: assert i < len(path) path = path[i:] # Find extension i = path.rfind('.') if i > 0: assert i < len(path) ext = path[i:].lower() if self._extensions.has_key(ext): self._extensions[ext] = self._extensions[ext] + 1 else: self._extensions[ext] = 1 else: if self._extensions.has_key('(no extension)'): self._extensions['(no extension)'] = self._extensions[ '(no extension)'] + 1 else: self._extensions['(no extension)'] = 1 #end def Consume def Log(self): """ Dump out stats to the output. """ if len(self._extensions): output.Log('Count of file extensions on URLs:', 1) set = self._extensions.keys() set.sort() for ext in set: output.Log(' %7d %s' % (self._extensions[ext], ext), 1) #end def Log class Sitemap(xml.sax.handler.ContentHandler): """ This is the big workhorse class that processes your inputs and spits out sitemap files. It is built as a SAX handler for set up purposes. That is, it processes an XML stream to bring itself up. """ def __init__(self, suppress_notify): xml.sax.handler.ContentHandler.__init__(self) self._filters = [] # Filter objects self._inputs = [] # Input objects self._urls = {} # Maps URLs to count of dups self._set = [] # Current set of URLs self._filegen = None # Path generator for output files self._wildurl1 = None # Sitemap URLs to filter out self._wildurl2 = None # Sitemap URLs to filter out self._sitemaps = 0 # Number of output files # We init _dup_max to 2 so the default priority is 0.5 instead of 1.0 self._dup_max = 2 # Max number of duplicate URLs self._stat = PerURLStatistics() # Some simple stats self._in_site = False # SAX: are we in a Site node? self._in_Site_ever = False # SAX: were we ever in a Site? self._default_enc = None # Best encoding to try on URLs self._base_url = None # Prefix to all valid URLs self._store_into = None # Output filepath self._suppress = suppress_notify # Suppress notify of servers #end def __init__ def ValidateBasicConfig(self): """ Verifies (and cleans up) the basic user-configurable options. """ all_good = True if self._default_enc: encoder.SetUserEncoding(self._default_enc) # Canonicalize the base_url if all_good and not self._base_url: output.Error('A site needs a "base_url" attribute.') all_good = False if all_good and not URL.IsAbsolute(self._base_url): output.Error('The "base_url" must be absolute, not relative: %s' % self._base_url) all_good = False if all_good: self._base_url = URL.Canonicalize(self._base_url) if not self._base_url.endswith('/'): self._base_url = self._base_url + '/' output.Log('BaseURL is set to: %s' % self._base_url, 2) # Load store_into into a generator if all_good: if self._store_into: self._filegen = FilePathGenerator() if not self._filegen.Preload(self._store_into): all_good = False else: output.Error('A site needs a "store_into" attribute.') all_good = False # Ask the generator for patterns on what its output will look like if all_good: self._wildurl1 = self._filegen.GenerateWildURL(self._base_url) self._wildurl2 = self._filegen.GenerateURL(SITEINDEX_SUFFIX, self._base_url) # Unify various forms of False if all_good: if self._suppress: if (type(self._suppress) == types.StringType) or (type(self._suppress) == types.UnicodeType): if (self._suppress == '0') or (self._suppress.lower() == 'false'): self._suppress = False # Done if not all_good: output.Log('See "example_config.xml" for more information.', 0) return all_good #end def ValidateBasicConfig def Generate(self): """ Run over all the Inputs and ask them to Produce """ # Run the inputs for input in self._inputs: input.ProduceURLs(self.ConsumeURL) # Do last flushes if len(self._set): self.FlushSet() if not self._sitemaps: output.Warn('No URLs were recorded, writing an empty sitemap.') self.FlushSet() # Write an index as needed if self._sitemaps > 1: self.WriteIndex() # Notify self.NotifySearch() # Dump stats self._stat.Log() #end def Generate def ConsumeURL(self, url, allow_fragment): """ All per-URL processing comes together here, regardless of Input. Here we run filters, remove duplicates, spill to disk as needed, etc. """ if not url: return # Validate if not url.Validate(self._base_url, allow_fragment): return # Run filters accept = None for filter in self._filters: accept = filter.Apply(url) if accept != None: break if not (accept or (accept == None)): url.Log(prefix='FILTERED', level=2) return # Ignore our out output URLs if fnmatch.fnmatchcase(url.loc, self._wildurl1) or fnmatch.fnmatchcase( url.loc, self._wildurl2): url.Log(prefix='IGNORED (output file)', level=2) return # Note the sighting hash = url.MakeHash() if self._urls.has_key(hash): dup = self._urls[hash] if dup > 0: dup = dup + 1 self._urls[hash] = dup if self._dup_max < dup: self._dup_max = dup url.Log(prefix='DUPLICATE') return # Acceptance -- add to set self._urls[hash] = 1 self._set.append(url) self._stat.Consume(url) url.Log() # Flush the set if needed if len(self._set) >= MAXURLS_PER_SITEMAP: self.FlushSet() #end def ConsumeURL def FlushSet(self): """ Flush the current set of URLs to the output. This is a little slow because we like to sort them all and normalize the priorities before dumping. """ # Sort and normalize output.Log('Sorting and normalizing collected URLs.', 1) self._set.sort() for url in self._set: hash = url.MakeHash() dup = self._urls[hash] if dup > 0: self._urls[hash] = -1 if not url.priority: url.priority = '%.4f' % (float(dup) / float(self._dup_max)) # Get the filename we're going to write to filename = self._filegen.GeneratePath(self._sitemaps) if not filename: output.Fatal('Unexpected: Couldn\'t generate output filename.') self._sitemaps = self._sitemaps + 1 output.Log('Writing Sitemap file "%s" with %d URLs' % (filename, len(self._set)), 1) # Write to it frame = None file = None try: if self._filegen.is_gzip: basename = os.path.basename(filename); frame = open(filename, 'wb') file = gzip.GzipFile(fileobj=frame, filename=basename, mode='wt') else: file = open(filename, 'wt') file.write(SITEMAP_HEADER) for url in self._set: url.WriteXML(file) file.write(SITEMAP_FOOTER) file.close() if frame: frame.close() frame = None file = None except IOError: output.Fatal('Couldn\'t write out to file: %s' % filename) os.chmod(filename, 0644) # Flush self._set = [] #end def FlushSet def WriteIndex(self): """ Write the master index of all Sitemap files """ # Make a filename filename = self._filegen.GeneratePath(SITEINDEX_SUFFIX) if not filename: output.Fatal('Unexpected: Couldn\'t generate output index filename.') output.Log('Writing index file "%s" with %d Sitemaps' % (filename, self._sitemaps), 1) # Make a lastmod time lastmod = TimestampISO8601(time.time()) # Write to it try: fd = open(filename, 'wt') fd.write(SITEINDEX_HEADER) for mapnumber in range(0,self._sitemaps): # Write the entry mapurl = self._filegen.GenerateURL(mapnumber, self._base_url) mapattributes = { 'loc' : mapurl, 'lastmod' : lastmod } fd.write(SITEINDEX_ENTRY % mapattributes) fd.write(SITEINDEX_FOOTER) fd.close() fd = None except IOError: output.Fatal('Couldn\'t write out to file: %s' % filename) os.chmod(filename, 0644) #end def WriteIndex def NotifySearch(self): """ Send notification of the new Sitemap(s) to the search engines. """ if self._suppress: output.Log('Search engine notification is suppressed.', 1) return output.Log('Notifying search engines.', 1) # Override the urllib's opener class with one that doesn't ignore 404s class ExceptionURLopener(urllib.FancyURLopener): def http_error_default(self, url, fp, errcode, errmsg, headers): output.Log('HTTP error %d: %s' % (errcode, errmsg), 2) raise IOError #end def http_error_default #end class ExceptionURLOpener old_opener = urllib._urlopener urllib._urlopener = ExceptionURLopener() # Build the URL we want to send in if self._sitemaps > 1: url = self._filegen.GenerateURL(SITEINDEX_SUFFIX, self._base_url) else: url = self._filegen.GenerateURL(0, self._base_url) # Test if we can hit it ourselves try: u = urllib.urlopen(url) u.close() except IOError: output.Error('When attempting to access our generated Sitemap at the ' 'following URL:\n %s\n we failed to read it. Please ' 'verify the store_into path you specified in\n' ' your configuration file is web-accessable. Consult ' 'the FAQ for more\n information.' % url) output.Warn('Proceeding to notify with an unverifyable URL.') # Cycle through notifications # To understand this, see the comment near the NOTIFICATION_SITES comment for ping in NOTIFICATION_SITES: query_map = ping[3] query_attr = ping[5] query_map[query_attr] = url query = urllib.urlencode(query_map) notify = urlparse.urlunsplit((ping[0], ping[1], ping[2], query, ping[4])) # Send the notification output.Log('Notifying: %s' % ping[1], 1) output.Log('Notification URL: %s' % notify, 2) try: u = urllib.urlopen(notify) u.read() u.close() except IOError: output.Warn('Cannot contact: %s' % ping[1]) if old_opener: urllib._urlopener = old_opener #end def NotifySearch def startElement(self, tag, attributes): """ SAX processing, called per node in the config stream. """ if tag == 'site': if self._in_site: output.Error('Can not nest Site entries in the configuration.') else: self._in_site = True if not ValidateAttributes('SITE', attributes, ('verbose', 'default_encoding', 'base_url', 'store_into', 'suppress_search_engine_notify')): return verbose = attributes.get('verbose', 0) if verbose: output.SetVerbose(verbose) self._default_enc = attributes.get('default_encoding') self._base_url = attributes.get('base_url') self._store_into = attributes.get('store_into') if not self._suppress: self._suppress = attributes.get('suppress_search_engine_notify', False) self.ValidateBasicConfig() elif tag == 'filter': self._filters.append(Filter(attributes)) elif tag == 'url': self._inputs.append(InputURL(attributes)) elif tag == 'urllist': for attributeset in ExpandPathAttribute(attributes, 'path'): self._inputs.append(InputURLList(attributeset)) elif tag == 'directory': self._inputs.append(InputDirectory(attributes, self._base_url)) elif tag == 'accesslog': for attributeset in ExpandPathAttribute(attributes, 'path'): self._inputs.append(InputAccessLog(attributeset)) elif tag == 'sitemap': for attributeset in ExpandPathAttribute(attributes, 'path'): self._inputs.append(InputSitemap(attributeset)) else: output.Error('Unrecognized tag in the configuration: %s' % tag) #end def startElement def endElement(self, tag): """ SAX processing, called per node in the config stream. """ if tag == 'site': assert self._in_site self._in_site = False self._in_site_ever = True #end def endElement def endDocument(self): """ End of SAX, verify we can proceed. """ if not self._in_site_ever: output.Error('The configuration must specify a "site" element.') else: if not self._inputs: output.Warn('There were no inputs to generate a sitemap from.') #end def endDocument #end class Sitemap def ValidateAttributes(tag, attributes, goodattributes): """ Makes sure 'attributes' does not contain any attribute not listed in 'goodattributes' """ all_good = True for attr in attributes.keys(): if not attr in goodattributes: output.Error('Unknown %s attribute: %s' % (tag, attr)) all_good = False return all_good #end def ValidateAttributes def ExpandPathAttribute(src, attrib): """ Given a dictionary of attributes, return a list of dictionaries with all the same attributes except for the one named attrib. That one, we treat as a file path and expand into all its possible variations. """ # Do the path expansion. On any error, just return the source dictionary. path = src.get(attrib) if not path: return [src] path = encoder.MaybeNarrowPath(path); pathlist = glob.glob(path) if not pathlist: return [src] # If this isn't actually a dictionary, make it one if type(src) != types.DictionaryType: tmp = {} for key in src.keys(): tmp[key] = src[key] src = tmp # Create N new dictionaries retval = [] for path in pathlist: dst = src.copy() dst[attrib] = path retval.append(dst) return retval #end def ExpandPathAttribute def OpenFileForRead(path, logtext): """ Opens a text file, be it GZip or plain """ frame = None file = None if not path: return (frame, file) try: if path.endswith('.gz'): frame = open(path, 'rb') file = gzip.GzipFile(fileobj=frame, mode='rt') else: file = open(path, 'rt') if logtext: output.Log('Opened %s file: %s' % (logtext, path), 1) else: output.Log('Opened file: %s' % path, 1) except IOError: output.Error('Can not open file: %s' % path) return (frame, file) #end def OpenFileForRead def TimestampISO8601(t): """Seconds since epoch (1970-01-01) --> ISO 8601 time string.""" return time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(t)) #end def TimestampISO8601 def CreateSitemapFromFile(configpath, suppress_notify): """ Sets up a new Sitemap object from the specified configuration file. """ # Remember error count on the way in num_errors = output.num_errors # Rev up SAX to parse the config sitemap = Sitemap(suppress_notify) try: output.Log('Reading configuration file: %s' % configpath, 0) xml.sax.parse(configpath, sitemap) except IOError: output.Error('Cannot read configuration file: %s' % configpath) except xml.sax._exceptions.SAXParseException, e: output.Error('XML error in the config file (line %d, column %d): %s' % (e._linenum, e._colnum, e.getMessage())) except xml.sax._exceptions.SAXReaderNotAvailable: output.Error('Some installs of Python 2.2 did not include complete support' ' for XML.\n Please try upgrading your version of Python' ' and re-running the script.') # If we added any errors, return no sitemap if num_errors == output.num_errors: return sitemap return None #end def CreateSitemapFromFile def ProcessCommandFlags(args): """ Parse command line flags per specified usage, pick off key, value pairs All flags of type "--key=value" will be processed as __flags[key] = value, "--option" will be processed as __flags[option] = option """ flags = {} rkeyval = '--(?P\S*)[=](?P\S*)' # --key=val roption = '--(?P