PasteWebKit-1.0/0000775000175000017500000000000010516756100012143 5ustar ianbianbPasteWebKit-1.0/PasteWebKit.egg-info/0000775000175000017500000000000010516756100016017 5ustar ianbianbPasteWebKit-1.0/PasteWebKit.egg-info/PKG-INFO0000664000175000017500000000257310516756077017140 0ustar ianbianbMetadata-Version: 1.0 Name: PasteWebKit Version: 1.0 Summary: A port/reimplementation of Webware WebKit in WSGI and Paste Home-page: http://pythonpaste.org/webkit/ Author: Ian Bicking Author-email: ianb@colorstudy.com License: UNKNOWN Description: This is a reimplementation of the `Webware `_ API, using `Paste `_ for most of the functionality, and just providing an API wrapper. While the basic layout of applications is different from what Webware's ``MakeAppWorkDir`` creates, this is intended to be backward compatible for most typical Webware applications. See also the `Subversion repository `_ Keywords: web wsgi application framework webware webkit Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python Classifier: Topic :: Internet :: WWW/HTTP Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Internet :: WWW/HTTP :: WSGI Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application Classifier: Framework :: Paste PasteWebKit-1.0/PasteWebKit.egg-info/SOURCES.txt0000664000175000017500000000675710516756077017737 0ustar ianbianbsetup.cfg setup.py PasteWebKit.egg-info/PKG-INFO PasteWebKit.egg-info/SOURCES.txt PasteWebKit.egg-info/dependency_links.txt PasteWebKit.egg-info/entry_points.txt PasteWebKit.egg-info/namespace_packages.txt PasteWebKit.egg-info/not-zip-safe PasteWebKit.egg-info/requires.txt PasteWebKit.egg-info/top_level.txt docs/index.txt docs/paste-webkit.txt paste/__init__.py paste/webkit/__init__.py paste/webkit/servlet_script.py paste/webkit/templates.py paste/webkit/wkapplication.py paste/webkit/wkcommon.py paste/webkit/wkrequest.py paste/webkit/wkresponse.py paste/webkit/wkservlet.py paste/webkit/wksession.py paste/webkit/wktransaction.py paste/webkit/wsgiapp.py paste/webkit/FakeWebware/README.txt paste/webkit/FakeWebware/__init__.py paste/webkit/FakeWebware/MiscUtils/CSVJoiner.py paste/webkit/FakeWebware/MiscUtils/CSVParser.py paste/webkit/FakeWebware/MiscUtils/DBPool.py paste/webkit/FakeWebware/MiscUtils/DataTable.py paste/webkit/FakeWebware/MiscUtils/DateInterval.py paste/webkit/FakeWebware/MiscUtils/DictForArgs.py paste/webkit/FakeWebware/MiscUtils/Error.py paste/webkit/FakeWebware/MiscUtils/Funcs.py paste/webkit/FakeWebware/MiscUtils/M2PickleRPC.py paste/webkit/FakeWebware/MiscUtils/MixIn.py paste/webkit/FakeWebware/MiscUtils/NamedValueAccess.py paste/webkit/FakeWebware/MiscUtils/ParamFactory.py paste/webkit/FakeWebware/MiscUtils/PickleCache.py paste/webkit/FakeWebware/MiscUtils/PickleRPC.py paste/webkit/FakeWebware/MiscUtils/PropertiesObject.py paste/webkit/FakeWebware/MiscUtils/__init__.py paste/webkit/FakeWebware/TaskKit/Properties.py paste/webkit/FakeWebware/TaskKit/Scheduler.py paste/webkit/FakeWebware/TaskKit/Task.py paste/webkit/FakeWebware/TaskKit/TaskHandler.py paste/webkit/FakeWebware/TaskKit/__init__.py paste/webkit/FakeWebware/TaskKit/Docs/GenIndex.css paste/webkit/FakeWebware/TaskKit/Docs/QuickStart.html paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-0.1.1.html paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-0.7.html paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-0.8.html paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-X.Y.html paste/webkit/FakeWebware/TaskKit/Docs/StyleSheet.css paste/webkit/FakeWebware/TaskKit/Docs/TODO-TaskKit.text paste/webkit/FakeWebware/TaskKit/Docs/UsersGuide.html paste/webkit/FakeWebware/TaskKit/Docs/index.html paste/webkit/FakeWebware/TaskKit/Tests/BasicTest.py paste/webkit/FakeWebware/TaskKit/Tests/Test.py paste/webkit/FakeWebware/TaskKit/Tests/__init__.py paste/webkit/FakeWebware/WebKit/Application.py paste/webkit/FakeWebware/WebKit/HTTPExceptions.py paste/webkit/FakeWebware/WebKit/HTTPServlet.py paste/webkit/FakeWebware/WebKit/Page.py paste/webkit/FakeWebware/WebKit/__init__.py paste/webkit/FakeWebware/WebUtils/Funcs.py paste/webkit/FakeWebware/WebUtils/__init__.py paste/webkit/examples/EchoServlet.py paste/webkit/examples/__init__.py paste/webkit/paster_templates/blank_servlet.py_tmpl paste/webkit/paster_templates/webkit/+package+/sitepage.py_tmpl paste/webkit/paster_templates/webkit/+package+/wsgiapp.py_tmpl paste/webkit/paster_templates/webkit/+package+/web/__init__.py_tmpl paste/webkit/paster_templates/webkit/+package+/web/index.py_tmpl paste/webkit/paster_templates/webkit/+project+.egg-info/sqlobject.txt_tmpl paste/webkit/paster_templates/webkit/tests/conftest.py_tmpl paste/webkit/paster_templates/webkit/tests/fixture.py_tmpl tests/conftest.py tests/fixture.py tests/test_deploy.py tests/sample_apps/__init__.py tests/sample_apps/sample1/__init__.py tests/sample_apps/sample1/web/__init__.py tests/sample_apps/sample1/web/index.py tests/sample_configs/test_deploy.ini PasteWebKit-1.0/PasteWebKit.egg-info/dependency_links.txt0000664000175000017500000000000110516756077022102 0ustar ianbianb PasteWebKit-1.0/PasteWebKit.egg-info/entry_points.txt0000664000175000017500000000035310516756077021333 0ustar ianbianb [paste.app_factory] main=paste.webkit.wsgiapp:make_webkit_app [paste.paster_command] servlet=paste.webkit.servlet_script:ServletCommand [paste.paster_create_template] webkit=paste.webkit.templates:WebKit PasteWebKit-1.0/PasteWebKit.egg-info/namespace_packages.txt0000664000175000017500000000000610516756077022363 0ustar ianbianbpaste PasteWebKit-1.0/PasteWebKit.egg-info/not-zip-safe0000664000175000017500000000000110516756077020262 0ustar ianbianb PasteWebKit-1.0/PasteWebKit.egg-info/requires.txt0000664000175000017500000000003510516756077020432 0ustar ianbianbPasteDeploy Paste PasteScriptPasteWebKit-1.0/PasteWebKit.egg-info/top_level.txt0000664000175000017500000000000610516756077020562 0ustar ianbianbpaste PasteWebKit-1.0/docs/0000775000175000017500000000000010516756100013073 5ustar ianbianbPasteWebKit-1.0/docs/index.txt0000664000175000017500000000341210516756013014746 0ustar ianbianbPaste WebKit ============ :author: Ian Bicking :revision: $Rev: 5847 $ :date: $LastChangedDate: 2006-10-22 15:39:07 -0500 (Sun, 22 Oct 2006) $ .. contents:: About WebKit ------------ This is a reimplementation of the `Webware `_ WebKit servlet API. This implementation uses `WSGI `_ internally very heavily, and builds upon the framework-neutral tools and services in Paste. It also changes the installation and configuration process considerably, making this a more traditional Python package. However, *applications* are meant to be portable to this system with very little effort. You may encounter a few ``NotImplementedExceptions`` for some methods that seemed obscure, but API compatibility *is* a goal, and any problems can be `submitted as a bug `_. Status ------ Paste WebKit reimplements a stable API. Paste WebKit itself is very stable. This is primarily a bridge project for bringing old Webware code into the WSGI environment. No non-bugfix development on Paste WebKit is planned. Community --------- For questions about Webware programming in general, the webware-discuss@lists.sf.net list remains the best resource. You can `read the archives online `__. For questions related to installation, running the appserver, etc., you can email paste-users@pythonpaste.org, and `read those archives `__. Feel free to email the Paste list if in doubt. Documentation ------------- * `Installation and Configuration `_ * `Automatically-generated module index `_ PasteWebKit-1.0/docs/paste-webkit.txt0000664000175000017500000001253310516756013016242 0ustar ianbianbPaste WebKit ============ :author: Ian Bicking :revision: $Rev: 3287 $ :date: $LastChangedDate: 2005-09-26 12:08:24 -0500 (Mon, 26 Sep 2005) $ .. contents:: Introduction ------------ This is a basic description of how to get Paste WebKit installed and working. It is not a tutorial (see the `Todo Tutorial `_ for now -- but note the installation instructions here differ from that document). Installation ------------ To install, first get `easy_install `_. Currently, as not all prerequisites are fully packaged, do:: $ easy_install.py http://svn.pythonpaste.org/Paste/branches/mainline-refactor $ easy_install.py http://svn.pythonpaste.org/Paste/WebKit/trunk You may need to run these as root. The second command should install `Paste Deployment `_ on its own. Configuration ------------- This uses Paste Deploy, so to configure your application write a .ini file like:: [app:main] use = egg:PasteWebKit package_name = mypackage myconfig_var = foo1 There are several configuration keys WebKit uses. The first are to find your application; you can use *one* of these: ``servlet_directory``: The directory where you've put your servlets. You can use ``%(here)s`` to refer to the directory that contains the configuration file. ``package_name``: The package for your application. Servlets are expected in a ``web`` subpackage. So if you give ``mypackage`` it will look in ``mypackage.web`` for the servlets. The other keys configure various filters/middleware: ``complete_stack`` (default true): If false, then none of the middleware will be installed. This may be useful if you are nesting one WebKit app inside another (and don't want to duplicate the stack). ``debug`` (default false): This controls a number of settings. Mostly errors will be displayed in the browser with this on. It is also picked up from the global ``debug`` setting if not given. Also, with this on, the ``printdebug`` filter will be installed, which catches all print statements and shows them in a ``
``
    in each response page.

``cookie_name`` (default ``_SID_`` -- for session):

    The cookie name the session will use.

``session_file_path`` (default ``/tmp``):

    Where the session files are kept.

``error_email`` (default: none):

    Any email addresses that should be used when reporting errors.
    Adding emails here will enable this feature.  Use spaces to
    separate multiple addresses.

    If you don't give this in the application section, the keys
    ``error_email`` or ``sysadmin_email`` (in that order) will be
    pulled from te global configuration (``[DEFAULTS]``).

``error_log`` (default: none):

    A file to append to with error reports.

``show_exceptions_in_wsgi_errors`` (default: not debug):

    If true then errors will show up in the server error logs.  Where
    this goes depends on the server.  If debug is on, this defaults to
    off, and vice versa.

``from_address`` (default: ``errors@localhost``):

    What address errors appear to come from.

``smtp_server`` (default: ``localhost``):

    The SMTP server (for sending errors).

``error_subject_prefix`` (default: ``[app_name]``):

    The prefix to put on error email subjects.  If an ``app_name`` is
    given (globally or locally) then that will be used.

``error_message`` (default: none):

    Extra text to use in error messages presented to users (when debug
    mode is off)

``profile`` (default: false):

    If on, then all requests will be profiled.  This slows down the
    app considerable, so *absolutely* don't use it except in
    development.

``profile_limit`` (default: 40):

    Show the top N slowest parts of the system.

All other configuration keys will be sent to the application.  So in
the example ``myconfig_var`` would be application configuration.

Applications can access this information like::

    from paste.deploy import CONFIG
    
    def my_routine():
        var = CONFIG['myconfig_var']

The ``CONFIG`` variable dispatches to the configuration for the
*current request*, whatever that request might be.  This means you
can't use it at the module-level, because modules are loaded once,
before any per-request configuration is available.

Deploying an Application
------------------------

Once you've configured your application, you use Paste Deploy to
create a WSGI application.  WSGI applications can be used by many
servers.

To get an application you do::

    from paste.deploy import loadapp

    wsgi_app = loadapp('config:myconfig_file.ini',
                       relative_to=config_dir)

This loads the app described by ``app:main`` from the configuration
file.  You can give ``config:/absolute_path``, or use ``relative_to``
to allow relative paths.

You can also run the server with `PasteScript
`_.  Add a
``[server:main]`` section to your configuration, like::

    [server:main]
    # this is a good testing server:
    use = egg:PasteScript#wsgiutils
    host = 127.0.0.1
    port = 8080

And then use ``paster serve --reload path/to/conf.ini``.  The
``--reload`` option causes the server to restart when files are edited
(this is kind of expensive, so should only be used during
development).
PasteWebKit-1.0/paste/0000775000175000017500000000000010516756100013257 5ustar  ianbianbPasteWebKit-1.0/paste/webkit/0000775000175000017500000000000010516756100014544 5ustar  ianbianbPasteWebKit-1.0/paste/webkit/FakeWebware/0000775000175000017500000000000010516756100016727 5ustar  ianbianbPasteWebKit-1.0/paste/webkit/FakeWebware/MiscUtils/0000775000175000017500000000000010516756100020643 5ustar  ianbianbPasteWebKit-1.0/paste/webkit/FakeWebware/MiscUtils/CSVJoiner.py0000664000175000017500000000113710516756015023026 0ustar  ianbianbimport types


def joinCSVFields(fields):
	"""
	Returns a CSV record (eg a string) from a sequence of fields.
	Fields containing commands (,) or double quotes (") are quotes
	and double quotes are escaped (""). The terminating newline is
	NOT included.
	"""
	newFields = []
	for field in fields:
		assert type(field) is types.StringType
		if field.find('"')!=-1:
			newField = '"' + field.replace('"', '""') + '"'
		elif field.find(',')!=-1 or field.find('\n')!=-1 or field.find('\r')!=-1:
			newField = '"' + field + '"'
		else:
			newField = field
		newFields.append(newField)
	return ','.join(newFields)
PasteWebKit-1.0/paste/webkit/FakeWebware/MiscUtils/CSVParser.py0000664000175000017500000001437110516756015023040 0ustar  ianbianb# The states of the parser
StartRecord        = 0
StartField         = 1
InField            = 2
QuoteInField       = 3
InQuotedField      = 4
QuoteInQuotedField = 5
EndQuotedField     = 6

# State handlers can return Finished to terminate parsing early
Finished           = 10


class ParseError(Exception):
	pass


class CSVParser:
	"""
	Parses CSV files including all subtleties such as:
		* commas in fields
		* double quotes in fields
		* embedded newlines in fields
			- Examples of programs that produce such beasts include
			  MySQL and Excel

	For a higher-level, friendlier CSV class with many conveniences,
	see DataTable (which uses this class for its parsing).

	Example:
		records = []
		parse = CSVParser().parse
		for line in lines:
			results = parse(line)
			if results is not None:
				records.append(results)

	CREDIT

	The algorithm was taken directly from the open source Python
	C-extension, csv:
		http://www.object-craft.com.au/projects/csv/

	It would be nice to use the csv module when present, since it is
	substantially faster. Before that can be done, it needs to support
	allowComments and stripWhitespace, and pass the TestCSVParser.py
	test suite.
	"""

	def __init__(self, allowComments=1, stripWhitespace=1, fieldSep=',', autoReset=1, doubleQuote=1):
		"""
		@@ document
		"""
		# settings
		self._allowComments = allowComments
		self._stripWhitespace = stripWhitespace
		self._doubleQuote = doubleQuote
		self._fieldSep = fieldSep
		self._autoReset = autoReset

		# Other
		self._state = StartRecord
		self._fields = []
		self._hadParseError = 0
		self._field = []  # a list of chars for the cur field
		self.addChar = self._field.append

		# The handlers for the various states
		self._handlers = [
			self.startRecord,
			self.startField,
			self.inField,
			self.quoteInField,
			self.inQuotedField,
			self.quoteInQuotedField,
			self.endQuotedField,
		]


	## Parse ##

	def parse(self, line):
		"""
		Parse the single line and return a list or string fields, or
		None if the CSV record contains embedded newlines and the
		record is not yet complete.
		"""
		if self._autoReset and self._hadParseError:
			self.reset()
		handlers = self._handlers

		i = 0
		lineLen = len(line)
		while i=2:
			self._lock = threading.Lock()
			self._nextCon = 0
			self._connections = []
			self.addConnection = self._threadsafe_addConnection
			self.getConnection = self._threadsafe_getConnection
			self.returnConnection = self._threadsafe_returnConnection

		# @@ 2000-12-04 ce: Should we really make all the connections now?
		# Couldn't we do this on demand?
		for i in range(maxConnections):
			con = apply(dbModule.connect, args, kwargs)
			self.addConnection(con)


	# threadsafe/unthreadsafe refers to the database _module_, not THIS class..
	# this class is definitely threadsafe (um. that is, I hope so - Dan)

	def _threadsafe_addConnection(self, con):
		self._connections.append(con)


	def _threadsafe_getConnection(self):
		self._lock.acquire()
		try:
			con = PooledConnection(self, self._connections[self._nextCon])
			self._nextCon = self._nextCon + 1
			if self._nextCon >= len(self._connections):
				self._nextCon = 0
			return con
		finally:
			self._lock.release()

	def _threadsafe_returnConnection(self, con):
		return

	# These functions are used with DB modules that have connection level threadsafety, like PostgreSQL.
	#
	def _unthreadsafe_addConnection(self, con):
		self._queue.put(con)

	def _unthreadsafe_getConnection(self):
		return PooledConnection(self, self._queue.get())

	def _unthreadsafe_returnConnection(self, conpool):
		"""
		This should never be called explicitly outside of this module.
		"""
		self.addConnection(conpool._con)

PasteWebKit-1.0/paste/webkit/FakeWebware/MiscUtils/DataTable.py0000664000175000017500000005105210516756015023046 0ustar  ianbianb"""
DataTable.py


INTRODUCTION
------------

This class is useful for representing a table of data arranged by named
columns, where each row in the table can be thought of as a record::

	name   phoneNumber
	------ -----------
	Chuck  893-3498
	Bill   893-0439
	John   893-5901

This data often comes from delimited text files which typically
have well defined columns or fields with several rows each of which can
be thought of as a record.

Using a DataTable can be as easy as using lists and dictionaries::

	table = DataTable('users.csv')
	for row in table:
		print row['name'], row['phoneNumber']

Or even::

	table = DataTable('users.csv')
	for row in table:
		print '%(name)s %(phoneNumber)s' % row

The above print statement relies on the fact that rows can be treated
like dictionaries, using the column headings as keys.

You can also treat a row like an array::

	table = DataTable('something.tabbed', delimiter='\t')
	for row in table:
		for item in row:
			print item,
		print


COLUMNS
-------

Column headings can have a type specification like so:
	name, age:int, zip:int

Possible types include string, int, float and datetime. However,
datetime is not well supported right now.

String is assumed if no type is specified but you can set that
assumption when you create the table::

		table = DataTable(headings, defaultType='float')

Using types like int and float will cause DataTable to actually
convert the string values (perhaps read from a file) to these types
so that you can use them in natural operations. For example::

	if row['age']>120:
		self.flagData(row, 'age looks high')

As you can see, each row can be accessed as a dictionary with keys
according the column headings. Names are case sensitive.


ADDING ROWS
-----------

Like Python lists, data tables have an append() method. You can append
TableRecords, or you pass a dictionary, list or object, in which case a
TableRecord is created based on given values. See the method docs below
for more details.


FILES
-----

By default, the files that DataTable reads from are expected to be
comma-separated value files.

Limited comments are supported: A comment is any line whose very first
character is a #. This allows you to easily comment out lines in your
data files without having to remove them.

Whitespace around field values is stripped.

You can control all this behavior through the arguments found in the
initializer and the various readFoo() methods::

	...delimiter=',', allowComments=1, stripWhite=1

For example::

	table = DataTable('foo.tabbed', delimiter='\t', allowComments=0, stripWhite=0)

You should access these parameters by their name since additional ones
could appear in the future, thereby changing the order.

If you are creating these text files, we recommend the
comma-separated-value format, or CSV. This format is better defined
than the tab delimited format, and can easily be edited and manipulated
by popular spreadsheets and databases.


MICROSOFT EXCEL
---------------

On Microsoft Windows systems with Excel and the win32all package
(http://starship.python.net/crew/mhammond/), DataTable will use Excel
(via COM) to read ".xls" files.

::

  from MiscUtils import DataTable
  assert DataTable.canReadExcel()
  table = DataTable.DataTable('foo.xls')

With consistency to its CSV processing, DataTable will ignore any row
whose first cell is '#' and strip surrounding whitespace around strings.


TABLES FROM SCRATCH
-------------------

Here's an example that constructs a table from scratch:::

	table = DataTable(['name', 'age:int'])
	table.append(['John', 80])
	table.append({'name': 'John', 'age': 80})
	print table


QUERIES
-------

A simple query mechanism is supported for equality of fields::

	matches = table.recordsEqualTo({'uid': 5})
	if matches:
		for match in matches:
			print match
	else:
		print 'No matches.'


COMMON USES
-----------

* Programs can keep configuration and other data in simple comma-
  separated text files and use DataTable to access them. For example, a
  web site could read it's sidebar links from such a file, thereby
  allowing people who don't know Python (or even HTML) to edit these
  links without having to understand other implementation parts of the
  site.

* Servers can use DataTable to read and write log files.


FROM THE COMMAND LINE
---------------------

The only purpose in invoking DataTable from the command line is to see
if it will read a file::

  > python DataTable.py foo.csv

The data table is printed to stdout.


CACHING
-------

DataTable uses "pickle caching" so that it can read .csv files faster
on subsequent loads. You can disable this across the board with::

	from MiscUtils.DataTable import DataTable
	DataTable.usePickleCache = 0

Or per instance by passing "usePickleCache=0" to the constructor.

See the docstring of PickleCache.py for more information.


MORE DOCS
---------

Some of the methods in this module have worthwhile doc strings to look
at. See below.


TO DO
-----

* Allow callback parameter or setting for parsing CSV records.
* Perhaps TableRecord should inherit UserList and UserDict and override methods as appropriate...?
* Better support for datetime.
* _types and BlankValues aren't really packaged, advertised or
  documented for customization by the user of this module.
* DataTable:
	* Parameterize the TextColumn class.
	* Parameterize the TableRecord class.
	* More list-like methods such as insert()
	* writeFileNamed() is flawed: it doesn't write the table column
	  type
	* Should it inherit from UserList?
* Add error checking that a column name is not a number (which could
  cause problems).
* Look for various @@ tags through out the code.

"""


import os, string, sys
from CSVParser import CSVParser
from string import join, replace, split, strip
from types import *

try:
	StringTypes
except NameError:
	StringTypes = StringType

try:
	from MiscUtils import NoDefault
except ImportError:
	class NoDefault:
		pass

try:
	from mx.DateTime import DateTimeType, DateTimeFrom
except ImportError:
	pass


## Types ##

DateTimeType = ""
ObjectType = ""

_types = {
	'string':	StringType,
	'int':		IntType,
	'long':		LongType,
	'float':	FloatType,
	'datetime':	DateTimeType,
	'object':	ObjectType,
}


## Functions ##

def canReadExcel():
	try:
		from win32com.client import Dispatch
		Dispatch("Excel.Application")
	except:
		return False
	else:
		return True


## Classes ##


class DataTableError(Exception):
	pass


class TableColumn:
	"""
	A table column represents a column of the table including name and
	type.

	It does not contain the actual values of the column. These are
	stored individually in the rows.
	"""

	def __init__(self, spec):

		# spec is a string such as 'name' or 'name:type'
		fields = split(spec, ':')
		if len(fields)>2:
			raise DataTableError, 'Invalid column spec %s' % repr(spec)
		self._name = fields[0]

		if len(fields)==1:
			self._type = None
		else:
			self.setType(fields[1])

	def name(self):
		return self._name

	def type(self):
		return self._type

	def setType(self, type):
		""" Sets the type (by a string containing the name) of the heading. Usually invoked by DataTable to set the default type for columns whose types were not specified. """
		if type==None:
			self._type = None
		else:
			try:
				self._type = _types[type]
			except:
				raise DataTableError, 'Unknown type %s' % repr(type)

	def __repr__(self):
		return '<%s %s with %s at %x>' % (
			self.__class__.__name__, repr(self._name), repr(self._type), id(self))

	def __str__(self):
		return self._name


	## Utilities ##

	def valueForRawValue(self, rawValue):
		""" The rawValue is typically a string or value already of the appropriate type. TableRecord invokes this method to ensure that values (especially strings that come from files) are the correct types (e.g., ints are ints and floats are floats). """
		# @@ 2000-07-23 ce: an if-else ladder? perhaps these should be dispatched messages or a class hier
		if self._type is StringType:
			value = str(rawValue)
		elif self._type is IntType:
			if rawValue=='':
				value = 0
			else:
				value = int(rawValue)
		elif self._type is LongType:
			if rawValue=='':
				value = 0
			else:
				value = long(rawValue)
		elif self._type is FloatType:
			if rawValue=='':
				value = 0.0
			else:
				value = float(rawValue)
		elif self._type is DateTimeType:
			value = DateTimeFrom(rawValue)
		elif self._type is ObjectType:
			value = rawValue
		else:
			# no type set, leave values as they are
			value = rawValue
		return value


class DataTable:
	"""
	See the doc string for this module.
	"""

	usePickleCache = 1


	## Init ##

	def __init__(self, filenameOrHeadings=None, delimiter=',', allowComments=1, stripWhite=1, defaultType=None, usePickleCache=None):
		if usePickleCache is None:
			self.usePickleCache = self.usePickleCache  # grab the class-level attr
		else:
			self.usePickleCache = usePickleCache
		if defaultType and not _types.has_key(defaultType):
			raise DataTableError, 'Unknown type for default type: %s' % repr(defaultType)
		self._defaultType = defaultType
		self._filename = None
		self._headings = []
		self._rows = []
		if filenameOrHeadings:
			if type(filenameOrHeadings) is StringType:
				self.readFileNamed(filenameOrHeadings, delimiter, allowComments, stripWhite)
			else:
				self.setHeadings(filenameOrHeadings)


	## File I/O ##

	def readFileNamed(self, filename, delimiter=',', allowComments=1, stripWhite=1):
		self._filename = filename
		data = None
		if self.usePickleCache:
			from PickleCache import readPickleCache, writePickleCache
			data = readPickleCache(filename, pickleVersion=1, source='MiscUtils.DataTable')
		if data is None:
			if self._filename.endswith('.xls'):
				self.readExcel()
			else:
				file = open(self._filename, 'r')
				self.readFile(file, delimiter, allowComments, stripWhite)
				file.close()
			if self.usePickleCache:
				writePickleCache(self, filename, pickleVersion=1, source='MiscUtils.DataTable')
		else:
			self.__dict__ = data.__dict__
		return self

	def readFile(self, file, delimiter=',', allowComments=1, stripWhite=1):
		return self.readLines(file.readlines(), delimiter, allowComments, stripWhite)

	def readString(self, string, delimiter=',', allowComments=1, stripWhite=1):
		return self.readLines(split(string, '\n'), delimiter, allowComments, stripWhite)

	def readLines(self, lines, delimiter=',', allowComments=1, stripWhite=1):
		if self._defaultType is None:
			self._defaultType = 'string'
		haveReadHeadings = 0
		parse = CSVParser(fieldSep=delimiter, allowComments=allowComments, stripWhitespace=stripWhite).parse
		values = ''
		for line in lines:
			# process a row, either headings or data
			values = parse(line)
			if values:
				if haveReadHeadings:
					row = TableRecord(self, values)
					self._rows.append(row)
				else:
					self.setHeadings(values)
					haveReadHeadings = 1
		if values is None:
			raise DataTableError, "Unfinished multiline record."
		return self

	def canReadExcel(self):
		return canReadExcel()

	def readExcel(self):
		maxBlankRows = 10
		numRowsToReadPerCall = 20

		from win32com.client import Dispatch
		xl = Dispatch("Excel.Application")
		wb = xl.Workbooks.Open(os.path.abspath(self._filename))
		try:
			sh = wb.Worksheets(1)
			sh.Cells(1, 1)

			# determine max column
			numCols = 1
			while 1:
				if sh.Cells(1, numCols).Value in [None, '']:
					numCols -= 1
					break
				numCols += 1
			if numCols<=0:
				return

			def strip(x):
				try:
					return x.strip()
				except:
					return x

			# read rows of data
			maxCol = chr(ord('A') + numCols - 1)
			haveReadHeadings = 0
			rowNum = 1
			numBlankRows = 0
			valuesBuffer = {}   # keyed by row number
			while 1:
				try:
					# grab a single row
					values = valuesBuffer[rowNum]
				except KeyError:
					# woops. read buffer is out of fresh rows
					valuesRows = sh.Range('A%i:%s%i' % (rowNum, maxCol, rowNum+numRowsToReadPerCall-1)).Value
					valuesBuffer.clear()
					j = rowNum
					for valuesRow in valuesRows:
						valuesBuffer[j] = valuesRow
						j += 1
					values = valuesBuffer[rowNum]

				# non-"buffered" version, one row at a time:
				# values = sh.Range('A%i:%s%i' % (rowNum, maxCol, rowNum)).Value[0]

				values = [strip(v) for v in values]
				nonEmpty = [v for v in values if v]
				if nonEmpty:
					if values[0] not in ('#', u'#'):
						if haveReadHeadings:
							row = TableRecord(self, values)
							self._rows.append(row)
						else:
							self.setHeadings(values)
							haveReadHeadings = 1
					numBlankRows = 0
				else:
					numBlankRows += 1
					if numBlankRows>maxBlankRows:
						# consider end of spreadsheet
						break
				rowNum += 1
		finally:
			wb.Close()

	def save(self):
		self.writeFileNamed(self._filename)

	def writeFileNamed(self, filename):
		file = open(filename, 'w')
		self.writeFile(file)
		file.close()

	def writeFile(self, file):
		"""
		@@ 2000-07-20 ce: This doesn't write the column types (like :int) back out.
		@@ 2000-07-21 ce: It's notable that a blank numeric value gets read as zero and written out that way. Also, values None are written as blanks.
		"""

		# write headings
		file.write(join(map(lambda h: str(h), self._headings), ','))
		file.write('\n')

		def ValueWritingMapper(item):
			# So that None gets written as a blank and everything else as a string
			if item is None:
				return ''
			else:
				return str(item)

		# write rows
		for row in self._rows:
			file.write(join(map(ValueWritingMapper, row), ','))
			file.write('\n')

	def commit(self):
		if self._changed:
			self.save()
			self._changed = 0


	## Headings ##

	def heading(self, index):
		if type(key) is StringType:
			key = self._nameToIndexMap[key]
		return self._headings[index]

	def hasHeading(self, name):
		return self._nameToIndexMap.has_key(name)

	def numHeadings(self):
		return len(self._headings)

	def headings(self):
		return self._headings

	def setHeadings(self, headings):
		""" Headings can be a list of strings (like ['name', 'age:int']) or a list of TableColumns or None. """
		if not headings:
			self._headings = []
		elif isinstance(headings[0], StringTypes):
			self._headings = map(lambda h: TableColumn(h), headings)
		elif isinstance(headings[0], TableColumn):
			self._headings = list(headings)
		for heading in self._headings:
			if heading.type() is None:
				heading.setType(self._defaultType)
		self.createNameToIndexMap()


	## Row access (list like) ##

	def __len__(self):
		return len(self._rows)

	def __getitem__(self, index):
		return self._rows[index]

	def append(self, object):
		""" If object is not a TableRecord, then one is created, passing the object to initialize the TableRecord. Therefore, object can be a TableRecord, list, dictionary or object. See TableRecord for details. """

		if not isinstance(object, TableRecord):
			object = TableRecord(self, object)
		self._rows.append(object)
		self._changed = 1


	## Queries ##

	def recordsEqualTo(self, dict):
		records = []
		keys = dict.keys()
		for record in self._rows:
			matches = 1
			for key in keys:
				if record[key]!=dict[key]:
					matches = 0
					break
			if matches:
				records.append(record)
		return records


	## As a string ##

	def __repr__(self):
		# Initial info
		s = ['DataTable: %s\n%d rows\n' % (self._filename, len(self._rows))]

		# Headings
		s.append('     ')
		s.append(join(map(lambda h: str(h), self._headings), ', '))
		s.append('\n')

		# Records
		i = 0
		for row in self._rows:
			s.append('%3d. ' % i)
			s.append(join(map(lambda r: str(r), row), ', '))
			s.append('\n')
			i = i + 1
		return join(s, '')


	## As a dictionary ##

	def dictKeyedBy(self, key):
		""" Returns a dictionary containing the contents of the table indexed by the particular key. This is useful for tables that have a column which represents a unique key (such as a name, serial number, etc.). """
		dict = {}
		for row in self:
			dict[row[key]] = row
		return dict


	## Misc access ##

	def filename(self):
		return self._filename

	def nameToIndexMap(self):
		""" Table rows keep a reference to this map in order to speed up index-by-names (as in row['name']). """
		return self._nameToIndexMap


	## Self utilities ##

	def createNameToIndexMap(self):
		"""
		Invoked by self to create the nameToIndexMap after the table's
		headings have been read/initialized.
		"""
		map = {}
		for i in range(len(self._headings)):
			map[self._headings[i].name()] = i
		self._nameToIndexMap = map


# @@ 2000-07-20 ce: perhaps for each type we could specify a function to convert from string values to the values of the type

BlankValues = {
	StringType:   '',
	IntType:      0,
	FloatType:    0.0,
	DateTimeType: '',
	None:         None,
}


class TableRecord:

	## Init ##

	def __init__(self, table, values=None):
		"""
		Dispatches control to one of the other init methods based on the type of values.  Values can be one of three things:
			1. A TableRecord
			2. A list
			3. A dictionary
			4. Any object responding to hasValueForKey() and valueForKey().
		"""
		self._headings = table.headings()
		self._nameToIndexMap = table.nameToIndexMap()
		# @@ 2000-07-20 ce: Take out the headings arg to the init method since we have an attribute for that

		if values is not None:
			valuesType = type(values)
			if valuesType is ListType  or  valuesType is TupleType:
				# @@ 2000-07-20 ce: check for required attributes instead
				self.initFromSequence(values)
			elif valuesType is DictType:
				self.initFromDict(values)
			elif valuesType is InstanceType:
				self.initFromObject(value)
			else:
				raise DataTableError, 'Unknown type for values %s.' % valuesType

	def initFromSequence(self, values):
		if len(self._headings)=numValues:
				self._values.append(BlankValues[heading.type()])
			else:
				self._values.append(heading.valueForRawValue(values[i]))

	def initFromDict(self, dict):
		self._values = []
		for heading in self._headings:
			name = heading.name()
			if dict.has_key(name):
				self._values.append(heading.valueForRawValue(dict[name]))
			else:
				self._values.append(BlankValues[heading.type()])

	def initFromObject(self, object):
		"""
		The object is expected to response to hasValueForKey(name) and
		valueForKey(name) for each of the headings in the table. It's
		alright if the object returns 0 for hasValueForKey(). In that
		case, a "blank" value is assumed (such as zero or an empty
		string). If hasValueForKey() returns 1, then valueForKey() must
		return a value.
		"""
		self._values = []
		for heading in self._headings:
			name = heading.name()
			if object.hasValueForKey(name):
				self._values.append(heading.valueForRawValue(object.valueForKey(name)))
			else:
				self._values.append(BlankValues[heading.type()])


	## Accessing like a sequence or dictionary ##

	def __len__(self):
		return len(self._values)

	def __getitem__(self, key):
		if isinstance(key, StringTypes):
			key = self._nameToIndexMap[key]
		try:
			return self._values[key]
		except TypeError:
			raise TypeError, 'key=%r, key type=%r, self._values=%r' % (key, type(key), self._values)

	def __setitem__(self, key, value):
		if type(key) is StringType:
			key = self._nameToIndexMap[key]
		self._values[key] = value

	def __repr__(self):
		return '%s' % self._values

	def get(self, key, default=None):
		index = self._nameToIndexMap.get(key, None)
		if index is None:
			return default
		else:
			return self._values[index]

	def has_key(self, key):
		return self._nameToIndexMap.has_key(key)

	def keys(self):
		return self._nameToIndexMap.keys()

	def values(self):
		return self._values

	def items(self):
		items = []
		for key in self.keys():
			items.append((key, self[key]))
		return items


	## Additional access ##

	def asList(self):
		"""
		Returns a sequence whose values are the same at the record's
		and in the order defined by the table.
		"""
		# It just so happens that our implementation already has this
		return self._values[:]

	def asDict(self):
		""" Returns a dictionary whose key-values match the table record. """
		dict = {}
		nameToIndexMap = self._nameToIndexMap
		for key in nameToIndexMap.keys():
			dict[key] = self._values[nameToIndexMap[key]]
		return dict


	## valueForFoo() family ##

	def valueForKey(self, key, default=NoDefault):
		if default is NoDefault:
			return self[key]
		else:
			return self.get(key, default)

	def valueForAttr(self, attr, default=NoDefault):
		return self.valueForKey(attr['Name'], default)



def main(args=None):
	if args is None:
		args = sys.argv
	for arg in args[1:]:
		dt = DataTable(arg)
		print '*** %s ***' % arg
		print dt
		print


if __name__=='__main__':
	main()
PasteWebKit-1.0/paste/webkit/FakeWebware/MiscUtils/DateInterval.py0000664000175000017500000000335210516756015023607 0ustar  ianbianb"""
DateInterval.py

Convert interval strings (in the form of 1w2d, etc) to
seconds, and back again.  Is not exactly about months or
years (leap years in particular).

Accepts (y)ear, (b)month, (w)eek, (d)ay, (h)our, (m)inute, (s)econd.

Exports only timeEncode and timeDecode functions.  
"""

import re

second = 1
minute = second*60
hour = minute*60
day = hour*24
week = day*7
month = day*30
year = day*365
timeValues = {
    'y': year,
    'b': month,
    'w': week,
    'd': day,
    'h': hour,
    'm': minute,
    's': second,
    }
timeOrdered = timeValues.items()
timeOrdered.sort(lambda a, b: -cmp(a[1], b[1]))
    
def timeEncode(seconds):
    """Encodes a number of seconds (representing a time interval)
    into a form like 1h2d3s."""
    s = ''
    for char, amount in timeOrdered:
        if seconds >= amount:
            i, seconds = divmod(seconds, amount)
            s += '%i%s' % (i, char)
    return s

_timeRE = re.compile(r'[0-9]+[a-zA-Z]')
def timeDecode(s):
    """Decodes a number in the format 1h4d3m (1 hour, 3 days, 3 minutes)
    into a number of seconds"""
    time = 0
    for match in allMatches(s, _timeRE):
        char = match.group(0)[-1].lower()
        if not timeValues.has_key(char):
            # @@: should signal error
            continue
        time += int(match.group(0)[:-1]) * timeValues[char]
    return time

# @@-sgd 2002-12-23 - this function does not belong in this module, find a better place.
def allMatches(source, regex):
    """Return a list of matches for regex in source
    """
    pos = 0
    end = len(source)
    rv = []
    match = regex.search(source, pos)
    while match:
        rv.append(match)
        match = regex.search(source, match.end() )
    return rv

__all__ = [timeEncode, timeDecode]
PasteWebKit-1.0/paste/webkit/FakeWebware/MiscUtils/DictForArgs.py0000664000175000017500000001220210516756015023366 0ustar  ianbianb"""
DictForArgs.py


See the doc string for the DictForArgs() function.

Also, there is a test suite in Testing/TestDictForArgs.py
"""


import re, string


class DictForArgsError(Exception):
	pass

def _SyntaxError(s):
	raise DictForArgsError, 'Syntax error: %s' % repr(s)

def DictForArgs(s):
	"""
	Dictionary for arguments
	
	Takes an input such as::
	
			x=3
			name="foo"
			first='john' last='doe'
			required border=3

	And returns a dictionary representing the same. For keys that aren't
	given an explicit value (such as 'required' above), the value is '1'.

	All values are interpreted as strings. If you want ints and floats,
	you'll have to convert them yourself.

	This syntax is equivalent to what you find in HTML and close to other
	ML languages such as XML.

	Returns {} for an empty string.

	The informal grammar is::
	
		(NAME [=NAME|STRING])*

	Will raise DictForArgsError if the string is invalid.

	See also: PyDictForArgs() and ExpandDictWithExtras() in this module
	"""

	s = string.strip(s)

	# Tokenize

	# @@ 2001-09-29 ce: push these outside for better performance
	nameRE   = re.compile(r'\w+')
	equalsRE = re.compile(r'\=')
	stringRE = re.compile(r'''
					"[^"]+"|
					'[^']+'|
					\S+''', re.VERBOSE)    #'
	whiteRE  = re.compile(r'\s+')
	REs = [nameRE, equalsRE, stringRE, whiteRE]

	verbose = 0
	matches = []
	start   = 0
	sLen    = len(s)

	if verbose:
		print '>> DictForArgs(%s)' % repr(s)
		print '>> sLen:', sLen
	while start> try:', regEx
			match = regEx.match(s, start)
			if verbose: print '>> match:', match
			if match is not None:
				if match.re is not whiteRE:
					matches.append(match)
				start = match.end()
				if verbose: print '>> new start:', start
				break
		else:
			_SyntaxError(s)

	if verbose:
		names = []
		for match in matches:
			if match.re is nameRE:
				name = 'name'
			elif match.re is equalsRE:
				name = 'equals'
			elif match.re is stringRE:
				name = 'string'
			elif match.re is whiteRE:
				name = 'white'
			names.append(name)
			#print '>> match =', name, match
		print '>> names =', names


	# Process tokens

	# At this point we have a list of all the tokens (as re.Match objects)
	# We need to process these into a dictionary.

	dict = {}
	matchesLen = len(matches)
	i = 0
	while i>> err = Error(None, 'Too bad.', timestamp=time.time())
		>>> err.keys()
		['timestamp']

	Or include the values as a dictionary, instead of keyword arguments:
		>>> info = {'timestamp': time.time()}
		>>> err = Error(None, 'Too bad.', info)

	Or you could even do both if you needed to.
	"""

	def __init__(self, object, message, valueDict={}, **valueArgs):
		""" Initializes an error with the object the error occurred for, and the user-readable error message. The message should be self sufficient such that if printed by itself, the user would understand it. """
		UserDict.__init__(self)
		self._object    = object
		self._message   = message
		self.update(valueDict)
		self.update(valueArgs)

	def object(self):
		return self._object

	def message(self):
		return self._message

	def __repr__(self):
		return 'ERROR(object=%s; message=%s; data=%s)' % (repr(self._object), repr(self._message), repr(self.data))

	def __str__(self):
		return 'ERROR: %s' % self._message

	def __nonzero__(self):
		return 1
PasteWebKit-1.0/paste/webkit/FakeWebware/MiscUtils/Funcs.py0000664000175000017500000002277010516756015022310 0ustar  ianbianb"""
Funcs.py

Funcs.py, a member of MiscUtils, holds functions that don't fit in anywhere else.
"""

import md5, os, random, string, time, sys, tempfile
True, False = 1==1, 1==0


def commas(number):
	""" Returns the given number as a string with commas to separate the thousands positions. The number can be a float, int, long or string. Returns None for None. """
	if number is None:
		return None
	if not number:
		return str(number)
	number = list(str(number))
	if '.' in number:
		i = number.index('.')
	else:
		i = len(number)
	while 1:
		i = i-3
		if i<=0 or number[i-1]=='-':
			break
		number[i:i] = [',']
	return string.join(number, '')


def charWrap(s, width, hanging=0):
	""" Returns a new version of the string word wrapped with the given width and hanging indent. The font is assumed to be monospaced.
	This can be useful for including text between 
 
tags since
 will not word wrap and for lengthly lines, will increase the width of a web page.
	It can also be used to help delineate the entries in log-style output by passing hanging=4.
	"""
	import string
	if not s:
		return s
	assert hangingwidth:
			t = s[width:]
			s = s[:width]
			lines[i] = s
			i = i + 1
			lines.insert(i, None)
			s = hanging + t
		else:
			lines[i] = s
		i = i + 1
	return string.join(lines, '\n')

# Python 2.3 contains mktemp and mkstemp, both of which accept a
# directory argument.  Earlier versions of Python only contained
# mktemp which didn't accept a directory argument.  So we have to
# implement our own versions here.
if sys.version_info >= (2, 3, None, None):
	# Just use the Python 2.3 built-in versions.
	from tempfile import mktemp, mkstemp
else:
	def mktemp(suffix="", dir=None):
		"""
		User-callable function to return a unique temporary file name.

		Duplicated from Python's own tempfile with the optional "dir"
		argument added. This allows customization of the directory, without
		having to take over the module level variable, tempdir.
		"""
		if not dir: dir = tempfile.gettempdir()
		pre = tempfile.gettempprefix()
		while 1:
			i = tempfile._counter.get_next()
			file = os.path.join(dir, pre + str(i) + suffix)
			if not os.path.exists(file):
				return file

	def mkstemp(suffix="", dir=None):
		"""
		User-callable function to return a tuple containing:
			- a os-level file handle for the temp file, open for read/write
			- the absolute path of that file

		Note that this version of the function is not as secure as the
		version included in Python 2.3.
		"""
		path = mktemp(suffix, dir)
		return os.open(path, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0600), path

def wordWrap(s, width=78):
	"""
	Returns a version of the string word wrapped to the given width.
	Respects existing newlines in the string.

	Taken from:
	http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
	"""
	return reduce(
		lambda line, word, width=width: "%s%s%s" % (
			line,
			' \n'[(len(line[line.rfind('\n')+1:]) + len(word) >= width)],
			word
		),
		s.split(' ')
	)


def dateForEmail(now=None):
	""" Returns a properly formatted date/time string for email messages """
	if now is None:
		now = time.localtime(time.time())
	if now[8]==1:
		offset = -time.altzone / 60
	else:
		offset = -time.timezone / 60
	if offset<0:
		plusminus = '-'
	else:
		plusminus = '+'
	return time.strftime('%a, %d %b %Y %H:%M:%S ', now) + plusminus + '%02d%02d' % (abs(offset/60), abs(offset%60))


def hostName():
	"""
	Returns the host name which is taken first from the os environment and failing that, from the 'hostname' executable. May return None if neither attempt succeeded.
	The environment keys checked are HOST and HOSTNAME both upper and lower case.
	"""
	for name in ['HOST', 'HOSTNAME', 'host', 'hostname']:
		hostName = os.environ.get(name, None)
		if hostName:
			break
	if not hostName:
		hostName = string.strip(os.popen('hostname').read())
	if not hostName:
		hostName = None
	else:
		hostName = string.lower(hostName)
	return hostName


_localIP = None

def localIP(remote=('www.yahoo.com', 80), useCache=1):
	"""
	Gets the "public" address of the local machine, i.e. that address
	which is connected to the general Internet.

	This function connects to a remote HTTP server the first time it is
	invoked (or every time it is invoked with useCache=0). If that is
	not acceptable, pass remote=None, but be warned that the result is
	less likely to be externally visible.

	Getting your local ip is actually quite complex. If this function
	is not serving your needs then you probably need to think deeply
	about what you really want and how your network is really set up.
	Search comp.lang.python for "local ip" for more information.
	http://groups.google.com/groups?q=%22local+ip%22+group:comp.lang.python.*
	"""
	global _localIP
	if useCache and _localIP:
		return _localIP
	import socket
	if remote:
		# code from Donn Cave on comp.lang.python

		# My notes:
		# Q: Why not use this?  socket.gethostbyname(socket.gethostname())
		# A: On some machines, it returns '127.0.0.1' - not what we had in mind.
		#
		# Q: Why not use this?  socket.gethostbyname_ex(socket.gethostname())[2]
		# A: Because some machines have more than one IP (think "VPN", etc.) and
		#    there is no easy way to tell which one is the externally visible IP.

		try:
			s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
			s.connect(remote)
			ip, port = s.getsockname()
			s.close()
			_localIP = ip
			return _localIP
		except socket.error:
			# oh, well. we'll use the local method
			pass

	addresses = socket.gethostbyname_ex(socket.gethostname())[2]
	for address in addresses:
		if address!='127.0.0.1':
			if useCache:
				_localIP = address
			return address
	if useCache:
		_localIP = addresses[0]
	return _localIP


def safeDescription(x, what='what'):
	"""
	Returns the repr() of x and its class (or type) for help in
	debugging. A major benefit here is that exceptions from
	repr() are consumed. This is important in places like
	"assert" where you don't want to lose the assertion
	exception in your attempt to get more information.

	Example use:
	assert isinstance(foo, Foo), safeDescription(foo)
	print "foo:", safeDescription(foo)  # won't raise exceptions

	# better output format:
	assert isinstance(foo, Foo), safeDescription(foo, 'foo')
	print safeDescription(foo, 'foo')
	"""
	try:
		xRepr = repr(x)
	except Exception, e:
		xRepr = _descExc('x', e)
	if hasattr(x, '__class__'):
		try:
			cRepr = repr(x.__class__)
		except Exception, e:
			cRepr = _descExc('x.__class__', e)
		return '%s=%s class=%s' % (what, xRepr, cRepr)
	else:
		try:
			cRepr = repr(type(x))
		except Exception, e:
			cRepr = _descExc('type(x)', e)
		return '%s=%s type=%s' % (what, xRepr, cRepr)

def _descExc(reprOfWhat, e):
	"""
	Returns a description of an exception. This is a private function
	for use by safeDescription().
	"""
	try:
		return '(exception from repr(%s): %s: %s)' % (reprOfWhat, e.__class__, e)
	except:
		return '(exception from repr(%s))' % reprOfWhat

def timestamp(numSecs=None):
	"""
	Returns a dictionary of timestamp versions.

	Returns different versions of the timestamp:
	
		'numSecs': the number of seconds
		'tuple': (year, month, day, hour, min, sec)
		'pretty': 'YYYY-MM-DD HH:MM:SS'
		'condensed': 'YYYYMMDDHHMMSS'
		'dashed': 'YYYY-MM-DD-HH-MM-SS'

	The focus is on the year, month, day, hour and second, with no additional information such as timezone or day of year. This form of timestamp is often ideal for print statements, logs and filenames.

	If the current number of seconds is not passed, then the current time is taken.

	The 'pretty' format is ideal for print statements, while the 'condensed' and 'dashed' formats are generally more appropriate for filenames.
	"""
	if numSecs is None:
		numSecs = time.time()
	tuple     = time.localtime(numSecs)[:6]
	pretty    = '%4i-%02i-%02i %02i:%02i:%02i' % tuple
	condensed = '%4i%02i%02i%02i%02i%02i' % tuple
	dashed    = '%4i-%02i-%02i-%02i-%02i-%02i' % tuple
	return locals()


def uniqueId(forObject=None):
	"""
	Generates an opaque, identifier string that is practically guaranteed to be unique.
	If an object is passed, then its id() is incorporated into the generation.
	Relies on md5 and returns a 32 character long string.
	"""
	r = [time.time(), random.random(), os.times()]
	if forObject is not None:
		r.append(id(forObject))
	md5object = md5.new(str(r))
	try:
		return md5object.hexdigest()
	except AttributeError:
		# Older versions of Python didn't have hexdigest, so we'll do it manually
		hexdigest = []
		for char in md5object.digest():
			hexdigest.append('%02x' % ord(char))
		return string.join(hexdigest, '')


def valueForString(s):
	"""
	For a given string, returns the most appropriate Pythonic value
	such as None, a long, an int, a list, etc. If none of those
	make sense, then returns the string as-is.

	"None", "True" and "False" are case-insensitive because there is
	already too much case sensitivity in computing, damn it!
	"""
	if not s:
		return s
	try:
		return int(s)
	except ValueError:
		pass
	try:
		return long(s)
	except ValueError:
		pass
	try:
		return float(s)
	except ValueError:
		pass
	t = s.lower()
	if t=='none':
		return None
	if t=='true':
		return True
	if t=='false':
		return False
	if s[0] in '[({"\'':
		return eval(s)
	return s


### Deprecated

def Commas(number):
	print 'DEPRECATED: MiscUtils.Funcs.Commas() on 02/23/01 in ver 0.5. Use commas() instead.'
	return commas(number)

def CharWrap(s, width, hanging=0):
	print 'DEPRECATED: MiscUtils.Funcs.CharWrap() on 02/23/01 in ver 0.5. Use charWrap() instead.'
	return charWrap(s, width, hanging)
PasteWebKit-1.0/paste/webkit/FakeWebware/MiscUtils/M2PickleRPC.py0000664000175000017500000000320610516756015023176 0ustar  ianbianb"""
M2Crypto-enhanced transport for PickleRPC

This lets you use M2Crypto for SSL encryption.

Based on m2xmlrpclib.py which is
	Copyright (c) 1999-2002 Ng Pheng Siong. All rights reserved
"""

from PickleRPC import Transport
import base64, string, sys
from M2Crypto import SSL, httpslib, m2urllib
try:
	from cStringIO import StringIO
except ImportError:
	from StringIO import StringIO

__version__ = 1   # version of M2PickleRPC

class M2Transport(Transport):
	user_agent = "M2PickleRPC.py/%s - %s" % (__version__, Transport.user_agent)

	def __init__(self, ssl_context=None):
		if ssl_context is None:
			self.ssl_ctx=SSL.Context('sslv23')
		else:
			self.ssl_ctx=ssl_context

	def make_connection(self, host):
		_host, _port = m2urllib.splitport(host)
		if sys.version[0] == '2':
			return httpslib.HTTPS(_host, int(_port), ssl_context=self.ssl_ctx)
		elif sys.version[:3] ==  '1.5':
			return httpslib.HTTPS(self.ssl_ctx, _host, int(_port))
		else:
			raise RuntimeError, 'unsupported Python version'

	# @@ workarounds below are necessary because M2Crypto seems to
	# return from fileobject.read() early!  So we have to call it
	# over and over to get the full data.
	
	def parse_response(self, f):
		"""
		Workaround M2Crypto issue mentioned above
		"""
		sio = StringIO()
		while 1:
			chunk = f.read()
			if not chunk:
				break
			sio.write(chunk)
		sio.seek(0)
		return Transport.parse_response(self, sio)

	def parse_response_gzip(self, f):
		"""
		Workaround M2Crypto issue mentioned above
		"""
		sio = StringIO()
		while 1:
			chunk = f.read()
			if not chunk:
				break
			sio.write(chunk)
		sio.seek(0)
		return Transport.parse_response_gzip(self, sio)

PasteWebKit-1.0/paste/webkit/FakeWebware/MiscUtils/MixIn.py0000664000175000017500000000730610516756015022254 0ustar  ianbianbfrom types import MethodType
import sys

if hasattr(sys, 'version_info') and sys.version_info[0]>=2:
	def MixIn(pyClass, mixInClass, makeAncestor=0, mixInSuperMethods=0):
		"""
		Mixes in the attributes of the mixInClass into the pyClass. These attributes are typically methods (but don't have to be). Note that private attributes, denoted by a double underscore, are not mixed in. Collisions are resolved by the mixInClass' attribute overwriting the pyClass'. This gives mix-ins the power to override the behavior of the pyClass.

		After using MixIn(), instances of the pyClass will respond to the messages of the mixInClass.

		An assertion fails if you try to mix in a class with itself.

		The pyClass will be given a new attribute mixInsForCLASSNAME which is a list of all mixInClass' that have ever been installed, in the order they were installed. You may find this useful for inspection and debugging.

		You are advised to install your mix-ins at the start up of your program, prior to the creation of any objects. This approach will result in less headaches. But like most things in Python, you're free to do whatever you're willing to live with.  :-)

		There is a bitchin' article in the Linux Journal, April 2001, "Using Mix-ins with Python" by Chuck Esterbrook which gives a thorough treatment of this topic.

		An example, that resides in Webware, is MiddleKit.Core.ModelUser.py, which install mix-ins for SQL adapters. Search for "MixIn(".

		If makeAncestor is 1, then a different technique is employed: the mixInClass is made the first base class of the pyClass. You probably don't need to use this and if you do, be aware that your mix-in can no longer override attributes/methods in pyClass.

		If mixInSuperMethods is 1, then support will be enabled for you to be able to call the original or 
		"parent" method from the mixed-in method.  This is done like so:

		    class MyMixInClass:
			def foo(self):
			    MyMixInClass.mixInSuperFoo(self)	# call the original method
			    # now do whatever you want

		This function only exists if you are using Python 2.0 or later. Python 1.5.2 has a problem where functions (as in aMethod.im_func) are tied to their class, when in fact, they should be totally generic with only the methods being tied to their class. Apparently this was fixed in Py 2.0.
		"""
		assert mixInClass is not pyClass, 'mixInClass = %r, pyClass = %r' % (mixInClass, pyClass)
		if makeAncestor:
			if mixInClass not in pyClass.__bases__:
				pyClass.__bases__ = (mixInClass,) + pyClass.__bases__
		else:
			# Recursively traverse the mix-in ancestor classes in order
			# to support inheritance
			baseClasses = list(mixInClass.__bases__)
			baseClasses.reverse()
			for baseClass in baseClasses:
				MixIn(pyClass, baseClass)

			# Track the mix-ins made for a particular class
			attrName = 'mixInsFor'+pyClass.__name__
			mixIns = getattr(pyClass, attrName, None)
			if mixIns is None:
				mixIns = []
				setattr(pyClass, attrName, mixIns)

			# Make sure we haven't done this before
			# Er, woops. Turns out we like to mix-in more than once sometimes.
			#assert not mixInClass in mixIns, 'pyClass = %r, mixInClass = %r, mixIns = %r' % (pyClass, mixInClass, mixIns)

			# Record our deed for future inspection
			mixIns.append(mixInClass)

			# Install the mix-in methods into the class
			for name in dir(mixInClass):
				if not name.startswith('__'): # skip private members
					member = getattr(mixInClass, name)

					if type(member) is MethodType and mixInSuperMethods:
						if hasattr(pyClass, name):
							origmember = getattr(pyClass, name)
							setattr(mixInClass, 'mixInSuper' + name[0].upper() + name[1:], origmember)
					if type(member) is MethodType:
						member = member.im_func
					setattr(pyClass, name, member)
PasteWebKit-1.0/paste/webkit/FakeWebware/MiscUtils/NamedValueAccess.py0000664000175000017500000004247410516756015024400 0ustar  ianbianb"""
NamedValueAccess provides functions, a mix-in class and a wrapper class
all for accessing Python objects by named attributes. You can use which
ever of the three approaches best suites your needs and style.


NOTES

If Python provided a root class 'Object' in the same tradition as other
OOP languages such as Smalltalk, Objective-C and Java, then we could
dispense with the global functions and simply stick with the mix-in.


TO DO

* The mix-in's valueForKey() could be out of slight alignment with the
  function, since they have different implementations. However, the test
  cases pass for both right now.

* Should the valueForKey() function provide for caching of bindings in
  the same manner than the mix-in does?

  If not, should the mix-in allow an option to *not* cache bindings?

* hasValueForKey() function? (We already have a method in the mix-in)

* valuesForNames() in the mix-in:
	* Change parameter 'keys' to 'names'
	* Use NoDefault instead of None in the parameters
	* Revisit doc string and test cases

* Docs: More improvs to doc strings.

* Testing: increase coverage

* Rename? class NamedValueAccess+ible:

* Benchmarking: Set this up in a new file:
	Testing/BenchNamedValueAccess.py
  so we can experment with caching vs. not and other techniques.


PAST DESIGN DECISIONS

* Only if a name binds to a method is it invoked. Another approach is
  to invoke any value that is __call__able, but that is unPythonic: If
  obj.foo is a class or a function then obj.foo gives that class or
  function, not the result of invoking it. Method is the only
  convenience we provide, because that's one of the major points of
  providing this.


CREDIT

Chuck Esterbrook 
Tavis Rudd 
"""


import types
import string, sys
from time import time
from MiscUtils import NoDefault


# if technique is zero, use bound methods in the _kvGetBindings cache, otherwise use unbound
# @@ 2000-05-31 ce: after additional testing we can probably scorge the technique=0 allowance
technique = 1


## Exceptions ##

class NamedValueAccessError(LookupError): pass
class ValueForKeyError(NamedValueAccessError): pass


class NamedValueAccess:
	"""
	This class is intended to be ancestor class such that you can say:
		from NamedValueAccess import *
		age = someObj.valueForName("age")
		name = someObj.valueForName("info.fields.name")

	This can be useful in setups where you wish to textually refer to the objects
	in a program, such as an HTML template processed in the context of an
	object-oriented framework.

	Keys can be matched to either methods or ivars and with or without underscores.

	valueForName() can also traverse bona fide dictionaries (DictType).

	You can safely import * from this module. Only the NamedValueAccess class is exported
	(other than typical things like string and sys).

	There is no __init__() method and never will be.

	You can run the test suite by running this module as a program.

	You'll see the terms 'key' and 'name' in the class and its documentation. A 'key'
	is a single identifier such as 'foo'. A name could be key, or a qualified key,
	such as 'foo.bar.boo'. Names are generally more convenient and powerful, while
	key-oriented methods are more efficient and provide the atomic functionality that
	name-oriented methods are built upon. From a usage point of view, you normally
	just use the 'name' methods and forget about the 'key'.

	@@ 2000-05-21 ce: This class causes problems when used in WebKit for logging.
		Perhaps circular references?
		Involving self?
		Having to do with methods bound to their objects?

	@@ 2000-03-03 ce: document ivars

	@@ 2000-04-24 ce: Some classes like UserDict need to use getitem()
	instead of getattr() and don't need to deal with _bindingForGetKey().

	@@ 2000-05-31 ce: Rename this class to NamedValues, NamedValueAccess, ValuesByName

	@@ This class probably needs to be in MiscUtils, as it's being used in that way
	   while MiddleKit was intended for "enterprise/business objects".
	"""

	#
	# Accessing values by key
	#
	def hasValueForKey(self, key):
		"""	Returns true if the key is available, although that does not
			guarantee that there will not be errors caused by retrieving the key. """

		return self._bindingForGetKey(key)!=None


	def valueForKey(self, key, default=NoDefault):
		""" Suppose key is 'foo'. This method returns the value with the following precedence:
				1. Methods before non-methods
				2. Public attributes before private attributes

			More specifically, this method then returns one of the following:
				* self.foo()
				* self._foo()
				* self.foo
				* self._foo

			...or default, if it was specified,
			otherwise invokes and returns result of valueForUnknownKey().
			Note that valueForUnknownKey(), normally returns an exception.

			See valueForName() which is a more advanced version of this method that allows
			multiple, qualified keys.
		"""

		binding = self._bindingForGetKey(key)

		if not binding:
			if default is NoDefault:
				return self.valueForUnknownKey(key, default)
			else:
				return default

		if type(binding) is types.MethodType:
# @@ 2000-05-07 ce: come to a decision on exception handling for key errors
#			try:
			if technique:
				result = binding(self)
			else:
				result = binding()
#			except:
				# @@ 2000-02-18: Improve next line with exception info
#				raise NamedValueAccessError, 'Caught exception while accessing key (%s). Exception is %s' % (key, sys.exc_info())
			return result
		else:
			return getattr(self, binding)

	def hasValueForName(self, keysString):
		try:
			value = self.valueForName(keysString)
		except NamedValueAccessError:
			return 0
		return 1

	def valueForName(self, keysString, default=None):
		""" Returns the value for the given keysString. This is the more advanced version of
			valueForKey(), which can only handle single names. This method can handle
			'foo', 'foo1.foo2', 'a.b.c.d', etc. It will traverse dictionaries if needed. """
		keys = string.split(keysString, '.')
		return self.valueForKeySequence(keys, default)

	def valueForKeySequence(self, listOfKeys, default=None):
		# @@ 2000-02-18: document
		return _valueForKeySequence(self, listOfKeys, default)

	def valuesForNames(self, keys, default=None, defaults=None, forgive=0, includeNames=0):
		""" Returns a list of values that match the given keys, each of which is passed
			  through valueForName() and so could be of the form 'a.b.c'.
			keys is a sequence. default is any kind of object. defaults is a sequence.
			  forgive and includeNames is a flag.
			If default is not None, then it is substituted when a key is not found.
			Otherwise, if defaults is not None, then it's corresponding/parallel value
			  for the current key is substituted when a key is not found.
			Otherwise, if forgive=1, then unknown keys simply don't produce any values.
			Otherwise, if default and defaults are None, and forgive=0, then the unknown
			  keys will probably raise an exception through self.valueForUnknownKey() although
			  that method can always return a final, default value.
			if keys is None, then None is returned. If keys is an empty list, then None
			  is returned.
			Often these last four arguments are specified by key.
			Examples:
				names = ['origin.x', 'origin.y', 'size.width', 'size.height']
				obj.valuesForNames(names)
				obj.valuesForNames(names, default=0.0)
				obj.valuesForNames(names, defaults=[0.0, 0.0, 100.0, 100.0])
				obj.valuesForNames(names, forgive=0)
			@@ 2000-03-04 ce: includeNames is only supported when forgive=1.
				It should be supported for the other cases.
				It should be documented.
				It should be included in the test cases.
		"""

		if keys is None:
			return None
		if len(keys) is 0:
			return []
		results = []

		if default is not None:
			results = map(lambda key, myself=self, mydefault=default: myself.valueForName(key, mydefault), keys)
		elif defaults is not None:
			if len(keys) is not len(defaults):
				raise NamedValueAccessError, 'Keys and defaults have mismatching lengths (%d and %d).' % (len(keys), len(defaults))
			results = map(lambda key, default, myself=self: myself.valueForName(key, default), keys, defaults)
		elif forgive:
			results = []
			uniqueObject = 'uni' + 'que'
			for key in keys:
				value = self.valueForName(key, uniqueObject)
				if value is not uniqueObject:
					if includeNames:
						results.append((key, value))
					else:
						results.append(value)
		else:
			# no defaults, no forgiveness
			results = map(lambda key, myself=self: myself.valueForName(key), keys)
		return results

	def setValueForKey(self, key, value):
		# @@ 2000-02-18: naming might be weired here with args reversed
		""" Suppose key is 'foo'. This method sets the value with the following precedence:
				1. Public attributes before private attributes
				2. Methods before non-methods

			More specifically, this method then uses one of the following:
				@@ 2000-03-04 ce: fill in

			...or invokes handleUnknownSetKey().
		"""
		raise NotImplementedError # @@ 2000-03-04 ce

	def resetKeyBindings(self):
		# @@ 2000-02-18 document this method
		if hasattr(self, '_kvGetBindings'):
			self._kvGetBindings = {}


	#
	# Errors
	#
	def valueForUnknownKey(self, key, default):
		raise NamedValueAccessError, key

	#def handleUnknownSetKey(self, key):
	#	raise NamedValueAccessError, key


	#
	# Private
	#
	def _bindingForGetKey(self, key):
		"""	Bindings are cached.
			Bindings are methods or strings.
		"""

		# Make _kvGetBindings dictionary if we don't have one
		if not hasattr(self, '_kvGetBindings'):
			self._kvGetBindings = {}

		# Return the binding if we already have one
		if self._kvGetBindings.has_key(key):
			return self._kvGetBindings[key]

		# No binding, so we have to look for the key

		found = None  # set to what we find

		# Try plain old key
		if hasattr(self, key):
			found = getattr(self, key)
			#print '0: found = ', found, type(found)
			if type(found) is not types.MethodType:
				found = key
			elif technique:
				found = getattr(self.__class__, key)
			self._kvGetBindings[key] = found
		#print '1: found = ', found, type(found)

		# Try _key only if we didn't find a method called key
		if type(found) is not types.MethodType:
			underKey = '_' + key
			if hasattr(self, underKey):
				underAttr = getattr(self, underKey)
				if found==None:
					if type(underAttr) is types.MethodType:
						if technique:
							value = getattr(self.__class__, underKey)
						else:
							value = underAttr
					else:
						value = underKey
					found = self._kvGetBindings[key] = value
				else:
					if type(underAttr) is types.MethodType:
						if technique:
							underAttr = getattr(self.__class__, underKey)
						found = self._kvGetBindings[key] = underAttr

		#print '2: found = ', found, type(found)

		return found


class NamedValueAccessWrapper(NamedValueAccess):
	"""
	This provides a wrapper around an existing object which will respond
	to the methods of NamedValueAccess. By using the wrapper, you can
	stick with objects and methods such as obj.valueForName('x.y') (as
	opposed to functions like valueForName()) and refrain from modifying
	the existing class hierarchy with NamedValueAccess.

	Example:
		wrapper = NamedValueAccessWrapper(obj)
		print wrapper.valueForName('manager.name')
	"""

	def __init__(self, object):
		self._object = object

	def hasValueForKey(self, key):
		try:
			value = self.valueForKey(ley)
		except NamedValueAccessError:
			return 0
		else:
			return 1

	def valueForKey(self, key, default=NoDefault):
		return valueForKey(self._object)

	def valueForName(self, key, default=NoDefault):
		return valueForName(self._object)



#
# Private
#

def _valueForKeySequence(obj, listOfKeys, default=None):
	"""	This is a recursive function used to implement NamedValueAccess.valueForKeySequence.
		Besides supporting inheritors of NamedValueAccess, this function also supports
		dictionaries, which is why it's not found in the class.
	"""

	# @@ 2000-02-18: Optimize by specifying index instead of making new list
	if type(obj) is types.DictType:
		try:
			value = obj[listOfKeys[0]]
		except: # @@ 2000-03-03 ce: this exception should be more specific. probably nameerror or indexerror
			if default is None:
				raise NamedValueAccessError, 'Unknown key (%s) in dictionary.' % listOfKeys[0]
			else:
				return default
	else:
		value = obj.valueForKey(listOfKeys[0], default)
	if len(listOfKeys)>1:
		return _valueForKeySequence(value, listOfKeys[1:], default)
	else:
		return value


def _dict_valueForKey(obj, key, default=NoDefault):
	"""
	Returns the value for a given key of the dictionary-like object.
	This is a private, custom function built in support of valueForKey().
	"""
	try:
		value = obj[key]
	except AttributeError, e:
		# We attempt to pass only on exceptions caused
		# by obj not responding to __getitem__. Any
		# other exceptions generated get raised up.
		substring = "instance has no attribute '__getitem__'"
		if e.args[0][-len(substring):]==substring:
			if default is NoDefault:
				return None
			else:
				return
		else:
			raise
	except KeyError, e:
		if e.args[0]==key:
			if default is NoDefault:
				raise ValueForKeyError, key
			else:
				return default
		else:
			# If we get here, then the KeyError is deeper in the
			# implementation of obj[key]
			raise
	else:
		return value


def valueForKey(obj, key, default=NoDefault):
	"""
	Returns the value of the object named by the given key.

	Suppose key is 'foo'. This method returns the value with the
	following precedence:
		1. Methods before non-methods
		2. Attributes before keys (__getitem__)
		3. Public things before private things
		   (private being denoted by a preceding underscore)

	More specifically, this method returns one of the following:
		* obj.valueForKey(key)  # only if the method exists
		* obj.foo()
		* obj._foo()
		* obj.foo
		* obj._foo
		* obj['foo']
		* obj.valueForUnknownKey(key)
		* default  # only if specified

	If all of these fail, a ValueForKeyError is raised.


	NOTES

	* If the object provides a valueForKey() method, that method will be
	  invoked to do the work.

	* valueForKey() works on dictionaries and dictionary-like objects.

	* valueForUnknownKey() provides a hook by which objects can
	  delegate or chain their keyed value access to other objects.
	  The key and default arguments are passed to it and it should
	  generally respect the typical treatment of the the default
	  argument as found throughout Webware and described in the Style
	  Guidelines.

	* See valueForName() which is a more advanced version of this
	  function that allows multiple, qualified keys.
	"""

	# We only accept strings for keys
	assert type(key) is types.StringType

	# Use obj.valueForKey() if it is available
	valueForKeyMeth = getattr(obj, 'valueForKey', None)
	if valueForKeyMeth:
		return valueForKeyMeth(key, default)

	attr   = None
	method = None
	value  = None
	unknown = 0
	if type(obj) is types.DictType:
		if default is NoDefault:
			try:
				return obj[key]
			except KeyError:
				raise ValueForKeyError, key
		else:
			return obj.get(key, default)
	else:
		try:
			klass    = obj.__class__
		except AttributeError:
			raise AttributeError, '__class__ obj type=%r, obj=%r' % (type(obj), obj)
		method   = getattr(klass, key, None)
		if not method:
			underKey = '_' + key
			method = getattr(klass, underKey, None)
			if not method:
				attr = getattr(obj, key, NoDefault)
				if attr is NoDefault:
					attr = getattr(obj, underKey, NoDefault)
					if attr is NoDefault:
						getitem = getattr(obj.__class__, '__getitem__', None)
						if getitem:
							try:
								value = getitem(obj, key)
							except KeyError:
								unknown = 1

#	if value is not NoDefault:
#		return value
	if not unknown:
		if method:
			return method(obj)
		if attr is not NoDefault:
			return attr

	# Use obj.valueForUnknownKey() if it is available
	valueForUnknownKey = getattr(obj, 'valueForUnknownKey', None)
	if valueForUnknownKey:
		return valueForUnknownKey(key, default)

	if default!=NoDefault:
		return default
	else:
		raise ValueForKeyError, key


def valueForName(obj, name, default=NoDefault):
	"""
	Returns the value of the object that is named. The name can use
	dotted notation to traverse through a network/graph of objects.
	Since this function relies on valueForKey() for each individual
	component of the name, you should be familiar with the semantics
	of that notation.

	Example: valueForName(obj, 'department.manager.salary')
	"""

	names = string.split(name, '.')
	for name in names:
		obj = valueForKey(obj, name, default)
		if obj is default:
			return obj
		# 2001-04-19 ce: I suppose the above technique could result in
		# the default being returned prematurely if it was part of the
		# chain of names. Well, that's just the way it goes for now.
	return obj


# Beef up UserDict with the NamedValueAccess base class and custom versions of
# hasValueForKey() and valueForKey(). This all means that UserDict's (such as
# os.environ) are key/value accessible.
# @@ 2000-05-07 ce: CGIWrapper.py duplicates this.
def _enhanceUserDict():
	from UserDict import UserDict
	if not NamedValueAccess in UserDict.__bases__:
		UserDict.__bases__ = UserDict.__bases__ + (NamedValueAccess,)

		def _UserDict_hasValueForKey(self, key):
			return self.has_key(key)

		def _UserDict_valueForKey(self, key, default=NoDefault):
			if default is NoDefault:
				if self.has_key(key):
					return self[key]
				else:
					raise ValueForKeyError, key
			else:
				return self.get(key, default)

		setattr(UserDict, 'hasValueForKey', _UserDict_hasValueForKey)
		setattr(UserDict, 'valueForKey', _UserDict_valueForKey)

_enhanceUserDict()
PasteWebKit-1.0/paste/webkit/FakeWebware/MiscUtils/ParamFactory.py0000664000175000017500000000120110516756015023604 0ustar  ianbianbfrom threading import Lock

class ParamFactory:
    def __init__(self, klass, **extraMethods):
        self.lock = Lock()
        self.cache = {}
        self.klass = klass
        for name, func in extraMethods.items():
            setattr(self, name, func)
    def __call__(self, *args):
        self.lock.acquire()
        if not self.cache.has_key(args):
            value = self.klass(*args)
            self.cache[args] = value
            self.lock.release()
            return value
        else:
            self.lock.release()
            return self.cache[args]
    def allInstances(self):
        return self.cache.values()
    
PasteWebKit-1.0/paste/webkit/FakeWebware/MiscUtils/PickleCache.py0000664000175000017500000001571610516756015023367 0ustar  ianbianb"""
PickleCache provides tools for keeping fast-loading cached versions of
files so that subsequent loads are faster. This is similar to how Python
silently caches .pyc files next to .py files.

The typical scenario is that you have a type of text file that gets
"translated" to Pythonic data (dictionaries, tuples, instances, ints,
etc.). By caching the Python data on disk in pickle format, you can
avoid the expensive translation on subsequent reads of the file.

Two real life cases are MiscUtils.DataTable, which loads and represents
comma-separated files, and MiddleKit which has an object model file.
So for examples on using this module, load up the following files and
search for "Pickle":
	Webware/MiscUtils/DataTable.py
	MiddleKit/Core/Model.py

The cached file is named the same as the original file with
'.pickle.cache' suffixed. The utility of '.pickle' is to denote the file
format and the utilty of '.cache' is to provide '*.cache' as a simple
pattern that can be removed, ignored by backup scripts, etc.

The treatment of the cached file is silent and friendly just like
Python's approach to .pyc files. If it cannot be read or written for
various reasons (cache is out of date, permissions are bad, wrong python
version, etc.), then it will be silently ignored.


GRANULARITY

In constructing the test suite, I discovered that if the source file is
newly written less than 1 second after the cached file, then the fact
that the source file is newer will not be detected and the cache will
still be used. I believe this is a limitation of the granularity of
os.path.getmtime(). If anyone knows of a more granular solution, please
let me know.

This would only be a problem in programmatic situations where the source
file was rapidly being written and read. I think that's fairly rare.


PYTHON VERSION

These operations do nothing if you don't have Python 2.2 or greater.


SEE ALSO
	http://www.python.org/doc/current/lib/module-pickle.html

- wordwrap bar --------------------------------------------------------
"""

verbose = 0


import os, sys, time
from types import DictType
from pprint import pprint
try:
	from cPickle import load, dump
except ImportError:
	from pickle import load, dump

havePython22OrGreater = sys.version_info[0]>2 or (sys.version_info[0]==2 and sys.version_info[1]>=2)


s ="""
def readPickleCache(filename, pickleVersion=1, source=None, verbose=None):
	return _reader.read(filename, pickleVersion, source, verbose)

def writePickleCache(data, filename, pickleVersion=1, source=None, verbose=None):
	return _writer.write(data, filename, pickleVersion, source, verbose)
"""


class PickleCache:
	"""
	Just a simple abstract base class for PickleCacheReader and
	PickleCacheWriter.
	"""
	verbose = verbose

	def picklePath(self, filename):
		return filename + '.pickle.cache'


class PickleCacheReader(PickleCache):

	def read(self, filename, pickleVersion=1, source=None, verbose=None):
		"""
		Returns the data from the pickle cache version of the filename, if it can read. Otherwise returns None which also indicates that writePickleCache() should be subsequently called after the original file is read.
		"""
		if verbose is None:
			v = self.verbose
		else:
			v = verbose
		if v: print '>> PickleCacheReader.read() - verbose is on'
		assert filename

		if not os.path.exists(filename):
			#if v: print 'cannot find %r' % filename
			open(filename) # to get a properly constructed IOError

		if not havePython22OrGreater:
			#if v: print 'Python version is too old for this. Returning None.'
			return None

		didReadPickle = 0
		shouldDeletePickle = 0

		data = None

		picklePath = self.picklePath(filename)
		if os.path.exists(picklePath):
			if os.path.getmtime(picklePath)1:
								print 'display full dict:'
								pprint(dict)
							data = dict['data']
							didReadPickle = 1

		# delete the pickle file if suggested by previous conditions
		if shouldDeletePickle:
			try:
				#if v: print 'attempting to remove pickle cache file'
				os.remove(picklePath)
			except OSError, e:
				if v: print 'failed to remove: %s: %s' % (e.__class__.__name__, e)
				pass

		if v: print 'done reading data'; print

		return data


class PickleCacheWriter(PickleCache):

	writeSleepInterval = 0.1

	def write(self, data, filename, pickleVersion=1, source=None, verbose=None):
		if verbose is None:
			v = self.verbose
		else:
			v = verbose
		if v: print '>> PickleCacheWriter.write() - verbose is on'
		assert filename
		sourceTimestamp = os.path.getmtime(filename)

		if not havePython22OrGreater:
			#if v: print 'Python version is too old for this. Returning None.'
			return None

		picklePath = self.picklePath(filename)
		dict = {
			'source': source,
			'python version': sys.version_info,
			'pickle version': pickleVersion,
			'data': data,
		}
		if v>1:
			print 'display full dict:'
			pprint(dict)
		try:
			if v: print 'about to open for write %r' % picklePath
			file = open(picklePath, 'w')
		except IOError, e:
			if v: print 'error. not writing. %s: %s' % (e.__class__.__name__, e)
		else:
			while 1:
				dump(dict, file, 1)   # 1 = binary format
				file.close()
				# make sure the cache has a newer timestamp, otherwise the cache will
				# just get ignored and rewritten next time.
				if os.path.getmtime(picklePath)==sourceTimestamp:
					if v: print 'timestamps are identical. sleeping %0.2f seconds' % self.writeSleepInterval
					time.sleep(self.writeSleepInterval)
					file = open(picklePath, 'w')
				else:
					break

		if v: print 'done writing data'; print


# define module level convenience functions, readPickleCache and writePickleCache:

_reader = PickleCacheReader(); readPickleCache  = _reader.read
_writer = PickleCacheWriter(); writePickleCache = _writer.write
PasteWebKit-1.0/paste/webkit/FakeWebware/MiscUtils/PickleRPC.py0000664000175000017500000002740710516756015023010 0ustar  ianbianb"""
PickleRPC provides a Server object for connection to Pickle-RPC servers
for the purpose of making requests and receiving the responses.

	>>> from MiscUtils.PickleRPC import Server
	>>> server = Server('http://localhost/cgi-bin/WebKit.cgi/Examples/PickleRPCExample')
	>>> server.multiply(10,20)
	200
	>>> server.add(10,20)
	30


See also: Server, Webkit.PickleRPCServlet, WebKit.Examples.PickleRPCExample


UNDER THE HOOD

Requests look like this:
	{
		'version':    1,  # default
		'action':     'call',  # default
		'methodName': 'NAME',
		'args':       (A, B, ...), # default = (,)
		'keywords':   {'A': A, 'B': B, ...}  # default = {}
	}

Only 'methodName' is required since that is the only key without a
default value.

Responses look like this:
	{
		'timeReceived': N,
		'timeReponded': M,
		'value': V,
		'exception': E,
		'requestError': E,
	}

TimeReceived is the time the initial request was received.
TimeResponded is the time at which the response was finished, as
close to transmission as possible. The times are expressed as
number of seconds since the Epoch, e.g., time.time().

Value is whatever the method happened to return.

Exception may be 'occurred' to indicate that an exception
occurred, the specific exception, such as "KeyError: foo" or the
entire traceback (as a string), at the discretion of the server.
It will always be a non-empty string if it is present.

RequestError is an exception such as "Missing method
in request." (with no traceback) that indicates a problem with the
actual request received by the Pickle-RPC server.

Value, exception and requestError are all exclusive to each other.


SECURITY

Pickle RPC uses the SafeUnpickler class (in this module) to
prevent unpickling of unauthorized classes.  By default, it
doesn't allow _any_ classes to be unpickled.  You can override
allowedGlobals() or findGlobal() in a subclass as needed to
allow specific class instances to be unpickled.

Note that both Transport in this module and PickleRPCServlet in
WebKit are derived from SafeUnpickler.


CREDIT

The implementation of this module was taken directly from Python 2.2's
xmlrpclib and then transformed from XML-orientation to Pickle-orientation.

The zlib compression was adapted from code by Skip Montanaro that I found
here: http://manatee.mojam.com/~skip/python/
"""


__version__ = 1   # version of PickleRPC protocol

import types

try:
	from cPickle import dumps, Unpickler, UnpicklingError
except ImportError:
	from pickle import dumps, Unpickler, UnpicklingError

try:
	import zlib
except ImportError:
	zlib = None

try:
	from cStringIO import StringIO
except ImportError:
	from StringIO import StringIO

class Error(Exception):
	"""
	The abstract exception/error class for all PickleRPC errors.
	"""
	pass


class ResponseError(Error):
	"""
	These are unhandled exceptions raised when the server was computing
	a response. These will indicate errors such as:
		* exception in the actual target method on the server
		* malformed responses
		* non "200 OK" status code responses
	"""
	pass


# Sometimes xmlrpclib is installed as a package, sometimes not.  So we'll
# make sure it works either way.
try:
	from xmlrpclib.xmlrpclib import ProtocolError as _PE
except ImportError:
	from xmlrpclib import ProtocolError as _PE
# @@ 2002-01-31 ce: should this be caught somewhere for special handling? Perhaps in XMLRPCServlet?

class ProtocolError(ResponseError, _PE):
	pass


class RequestError(Error):
	"""
	These are errors originally raised by the server complaining about
	malformed requests.
	"""
	pass


class InvalidContentTypeError(ResponseError):

	def __init__(self, headers, content):
		Exception.__init__(self) #, headers, content)
		self.headers = headers
		self.content = content

	def __repr__(self):
		content = self.content
		return '%s: Content type is not text/x-python-pickled-dict\nheaders = %s\ncontent =\n%s' % (
			self.__class__.__name__, self.headers, content)

	__str__ = __repr__


class SafeUnpickler:
	"""
	For security reasons, we don't want to allow just anyone to unpickle
	anything.  That can cause arbitrary code to be executed.
	So this SafeUnpickler base class is used to control
	what can be unpickled.  By default it doesn't let you unpickle
	any class instances at all, but you can create subclass that
	overrides allowedGlobals().

	Note that the PickleRPCServlet class in WebKit is derived from this class
	and uses its load() and loads() methods to do all unpickling.
	"""
	def allowedGlobals(self):
		"""
		Must return a list of (moduleName, klassName) tuples for all
		classes that you want to allow to be unpickled.

		Example:
			return [('mx.DateTime', '_DT')]
		allows mx.DateTime instances to be unpickled.
		"""
		return []

	def findGlobal(self, module, klass):
		if (module, klass) not in self.allowedGlobals():
			raise UnpicklingError, 'For security reasons, you can\'t unpickle objects from module %s with type %s' % (module, klass)
		globals = {}
		exec 'from %s import %s as theClass' % (module, klass) in globals
		return globals['theClass']

	def load(self, file):
		safeUnpickler = Unpickler(file)
		safeUnpickler.find_global = self.findGlobal
		return safeUnpickler.load()

	def loads(self, str):
		return self.load(StringIO(str))


# @@ 2002-01-31 ce: Could we reduce code duplication and automatically
# inherit future improvements by actually importing and using the
# xmlrpclib classes below either as base classes or mix-ins?


class Server:
	"""uri [,options] -> a logical connection to an XML-RPC server

	uri is the connection point on the server, given as
	scheme://host/target.

	The standard implementation always supports the "http" scheme.  If
	SSL socket support is available (Python 2.0), it also supports
	"https".

	If the target part and the slash preceding it are both omitted,
	"/PickleRPC" is assumed.

	See the module doc string for more information.
	"""

	def __init__(self, uri, transport=None, verbose=0, binary=1, compressRequest=1, acceptCompressedResponse=1):
		# establish a "logical" server connection

		# get the url
		import urllib
		type, uri = urllib.splittype(uri)
		if type not in ("http", "https"):
			raise IOError, "unsupported Pickle-RPC protocol"
		self.__host, self.__handler = urllib.splithost(uri)
		if not self.__handler:
			self.__handler = "/PickleRPC"

		if transport is None:
			if type == "https":
				transport = SafeTransport()
			else:
				transport = Transport()
		self.__transport = transport

		self.__verbose = verbose
		self.__binary = binary
		self.__compressRequest = compressRequest
		self.__acceptCompressedResponse = acceptCompressedResponse

	def _request(self, methodName, args, keywords):
		"""
		Call a method on the remote server.
		"""
		request = {
			'version':    1,
			'action':     'call',
			'methodName': methodName,
			'args':       args,
			'keywords':   keywords,
		}
		if self.__binary:
			request = dumps(request, 1)
		else:
			request = dumps(request)
		if zlib is not None and self.__compressRequest and len(request) > 1000:
			request = zlib.compress(request, 1)
			compressed = 1
		else:
			compressed = 0

		response = self.__transport.request(
			self.__host,
			self.__handler,
			request,
			verbose=self.__verbose,
			binary=self.__binary,
			compressed=compressed,
			acceptCompressedResponse=self.__acceptCompressedResponse
			)

		return response

	def __requestValue(self, methodName, args, keywords):
		dict = self._request(methodName, args, keywords)
		if dict.has_key('value'):
			return dict['value']
		elif dict.has_key('exception'):
			raise ResponseError, dict['exception']
		elif dict.has_key('requestError'):
			raise RequestError, dict['requestError']
		else:
			raise RequestError, 'Response does not have a value, expection or requestError.'

	def __repr__(self):
		return "<%s for %s%s>" % (self.__class__.__name__, self.__host, self.__handler)

	__str__ = __repr__

	def __getattr__(self, name):
		# magic method dispatcher
		return _Method(self.__requestValue, name)

	## note: to call a remote object with an non-standard name, use
	## result getattr(server, "strange-python-name")(args)


ServerProxy = Server   # be like xmlrpclib for those who might guess or expect it



class _Method:
	"""
	Some magic to bind a Pickle-RPC method to an RPC server.
	Supports "nested" methods (e.g. examples.getStateName).
	"""

	def __init__(self, send, name):
		self.__send = send
		self.__name = name

	def __getattr__(self, name):
		return _Method(self.__send, "%s.%s" % (self.__name, name))

	def __call__(self, *args, **keywords):  # note that keywords are supported
		return self.__send(self.__name, args, keywords)


class Transport(SafeUnpickler):
	"""
	Handles an HTTP transaction to a Pickle-RPC server.
	"""

	# client identifier (may be overridden)
	user_agent = "PickleRPC/%s (by http://webware.sf.net/)" % __version__

	def request(self, host, handler, request_body, verbose=0, binary=0, compressed=0,
				acceptCompressedResponse=0):
		# issue Pickle-RPC request

		h = self.make_connection(host)
		if verbose:
			h.set_debuglevel(1)

		self.send_request(h, handler, request_body)
		self.send_host(h, host)
		self.send_user_agent(h)
		self.send_content(h, request_body, binary, compressed, acceptCompressedResponse)

		errcode, errmsg, headers = h.getreply()

		if errcode != 200:
			raise ProtocolError(
				host + handler,
				errcode, errmsg,
				headers
				)

		self.verbose = verbose

		if h.headers['content-type'] not in ['text/x-python-pickled-dict', 'application/x-python-binary-pickled-dict']:
			headers = h.headers.headers
			content = h.getfile().read()
			raise InvalidContentTypeError(headers, content)

		try:
			content_encoding = headers["content-encoding"]
			if content_encoding and content_encoding == "x-gzip":
				return self.parse_response_gzip(h.getfile())
			elif content_encoding:
				raise ProtocolError(host + handler,
									500,
									"Unknown encoding type: %s" %
									content_encoding,
									headers)
			else:
				return self.parse_response(h.getfile())
		except KeyError:
			return self.parse_response(h.getfile())

	def make_connection(self, host):
		# create a HTTP connection object from a host descriptor
		import httplib
		return httplib.HTTP(host)

	def send_request(self, connection, handler, request_body):
		connection.putrequest("POST", handler)

	def send_host(self, connection, host):
		connection.putheader("Host", host)

	def send_user_agent(self, connection):
		connection.putheader("User-Agent", self.user_agent)

	def send_content(self, connection, request_body, binary=0, compressed=0,
					 acceptCompressedResponse=0):
		if binary:
			connection.putheader("Content-Type", "application/x-python-binary-pickled-dict")
		else:
			connection.putheader("Content-Type", "text/x-python-pickled-dict")
		connection.putheader("Content-Length", str(len(request_body)))
		if compressed:
			connection.putheader("Content-Encoding", "x-gzip")
		if zlib is not None and acceptCompressedResponse:
			connection.putheader("Accept-Encoding", "gzip")
		connection.endheaders()
		if request_body:
			connection.send(request_body)

	def parse_response(self, f):
		return self.load(f)

	def parse_response_gzip(self, f):
		# read response from input file, decompress it, and parse it
		# @@ gat: could this be made more memory-efficient?
		return self.loads(zlib.decompress(f.read()))

class SafeTransport(Transport):
	"""
	Handles an HTTPS transaction to a Pickle-RPC server.
	"""

	def make_connection(self, host):
		# create a HTTPS connection object from a host descriptor
		# host may be a string, or a (host, x509-dict) tuple
		import httplib
		if isinstance(host, types.TupleType):
			host, x509 = host
		else:
			x509 = {}
		try:
			HTTPS = httplib.HTTPS
		except AttributeError:
			raise NotImplementedError,\
				  "your version of httplib doesn't support HTTPS"
		else:
			return apply(HTTPS, (host, None), x509)

	def send_host(self, connection, host):
		if isinstance(host, types.TupleType):
			host, x509 = host
		connection.putheader("Host", host)

PasteWebKit-1.0/paste/webkit/FakeWebware/MiscUtils/PropertiesObject.py0000664000175000017500000001263010516756015024507 0ustar  ianbianbfrom UserDict import UserDict
import os, string, sys, types

class WillNotRunError(Exception): pass


class PropertiesObject(UserDict):
	"""
	A PropertiesObject represents, in a dictionary-like fashion,
	the values found in a Properties.py file. That file is always
	included with a Webware component to advertise its name,
	version, status, etc. Note that a Webware component is a
	Python package that follows additional conventions. Also, the
	top level Webware directory contains a Properties.py.

	Component properties are often used for:
	
	* generation of documentation
	* runtime examination of components, especially prior to loading

	PropertiesObject provides additional keys:
	
	* filename - the filename from which the properties were read
	* versionString - a nicely printable string of the version
	* requiredPyVersionString - like versionString but for requiredPyVersion instead
	* willRun - 1 if the component will run. So far that means having the right Python version.
	* willNotRunReason - defined only if willRun is 0. contains a readable error message

	Using a PropertiesObject is better than investigating the Properties.py file directly, because the rules for determining derived keys and any future convenience methods will all be provided here.

	Usage example::

		from MiscUtils.PropertiesObject import PropertiesObject
		props = PropertiesObject(filename)
		for item in props.items():
			print '%s: %s' % item

	Note: We don't normally suffix a class name with "Object" as we have with this class, however, the name Properties.py is already used in our containing package and all other packages.
	"""


	## Init and reading ##

	def __init__(self, filename=None):
		UserDict.__init__(self)
		if filename:
			self.readFileNamed(filename)

	def loadValues(self, dict):
		self.update(dict)
		self.cleanPrivateItems()

		
	def readFileNamed(self, filename):
		self['filename'] = filename
		results = {}
		exec open(filename) in results
		# @@ 2001-01-20 ce: try "...in self"
		self.update(results)
		self.cleanPrivateItems()
		self.createDerivedItems()


	## Self utility ##

	def cleanPrivateItems(self):
		""" Removes items whose keys start with a double underscore, such as __builtins__. """
		for key in self.keys():
			if key[:2]=='__':
				del self[key]

	def createDerivedItems(self):
		self.createVersionString()
		self.createRequiredPyVersionString()
		self.createWillRun()

	def _versionString(self, version):
		""" For a sequence containing version information such as (2, 0, 0, 'pre'), this returns a printable string such as '2.0-pre'. The micro version number is only excluded from the string if it is zero. """
		ver = map(lambda x: str(x), version)
		if ver[2]=='0': # e.g., if minor version is 0
			numbers = ver[:2]
		else:
			numbers = ver[:3]
		rest = ver[3:]
		numbers = string.join(numbers, '.')
		rest = string.join(rest, '-')
		if rest:
			return numbers + rest
		else:
			return numbers

	def createVersionString(self):
		self['versionString'] = self._versionString(self['version'])

	def createRequiredPyVersionString(self):
		self['requiredPyVersionString'] = self._versionString(self['requiredPyVersion'])

	def createWillRun(self):
		self['willRun'] = 0
		try:
			# Invoke each of the checkFoo() methods
			for key in self.willRunKeys():
				methodName = 'check' + string.upper(key[0]) + key[1:]
				method = getattr(self, methodName)
				method()
		except WillNotRunError, msg:
			self['willNotRunReason'] = msg
			return
		self['willRun'] = 1  # we passed all the tests

	def willRunKeys(self):
		""" Returns a list of keys whose values should be examined in order to determine if the component will run. Used by createWillRun(). """
		return ['requiredPyVersion', 'requiredOpSys', 'deniedOpSys', 'willRunFunc']

	def checkRequiredPyVersion(self):
		pyVer = getattr(sys, 'version_info', None)
		if not pyVer:
			# Prior 2.0 there was no version_info
			# So we parse it out of .version which is a string
			pyVer = string.split(sys.version)[0]
			pyVer = string.split(pyVer, '.')
			pyVer = map(lambda x: int(x), pyVer)
		if tuple(pyVer)


PasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/Docs/QuickStart.html0000664000175000017500000005475410516756014024174 0ustar  ianbianb

TaskKit QuickStart




Tom's Webware Documentation

Scheduling with Python and Webware

Tom Schwaller


Since version 0.5 the web application framework Webware has a scheduling plug-in called TaskKit. This QuickStart Guide describes how to use it in your daily work with Webware and also with normal Python programs.

Scheduling periodic tasks is a very common activity for users of a modern operating system. System administrators for example know very well how to start new cron jobs or the corresponding Windows analogues. So, why does a web application server like Webware/WebKit need its own scheduling framework. The answer is simple: Because it knows better how to react to a failed job, has access to internal data structures, which otherwise would have to be exposed to the outside world and last but not least it needs scheduling capabilities anyway (e.g. for session sweeping and other memory cleaning operations).

Webware is developped with the object oriented scripting language Python so it seemed natural to write a general purpose Python based scheduling framework. One could think that this problem is already solved (remember the Python slogan: batteries included), but strange enough there has not much work been done in this area. The two standard Python modules sched.py and bisect.py are way too simple, not really object oriented and also not multithreaded. This was the reason to develop a new scheduling framework, which can not only be used with Webware but also with general purpose Python programs. Unfortunately scheduling has an annoying side effect. The more you delve into the subject the more it becomes difficult.

After some test implementations I discovered the Java scheduling framework of the Ganymede network directory management system and took it as a model for the Python implementation. Like any other Webware Kit or plug-in the TaskKit is self contained and can be used in other Python projects. This modularity is one of the real strengths of Webware and in sharp contrast to Zope where people tend to think in Zope and not in Python terms. In a perfect world one should be able to use web wrappers (for Zope, Webware, Quixote,..) around clearly designed Python classes and not be forced to use one framework. Time will tell if this is just a dream or if people will reinvent the "python wheels" over and over again.

Tasks

The TaskKit implements the three classes Scheduler, TaskHandler and Task. Let's begin with the simplest one, i.e. Task. It's an abstract base class, from which you have to derive your own task classes by overriding the run()-method like in the following example:

from TaskKit.Task import Task
from time import time, strftime, localtime

class SimpleTask(Task):
    def run(self):
      print self.name(), strftime("%H:%M:%S", localtime(time()))

self.name() returns the name under which the task was registered by the scheduler. It is unique among all tasks and scheduling tasks with the same name will delete the old task with that name (so beware of that feature!). Another simple example which is used by WebKit itself is found in WebKit/Tasks/SessionTask.py.

from TaskKit.Task import Task

class SessionTask(Task):
    def __init__(self, sessions):
        Task.__init__(self)
        self._sessionstore = sessions
        
    def run(self):
        if self.proceed():
            self._sessionstore.cleanStaleSessions(self)

Here you see the proceed() method in action. It can be used by long running tasks to check if they should terminate. This is the case when the scheduler or the task itself has been stoped. The latter is achieved with a stopTask() call which is not recommented though. It's generally better to let the task finish and use the unregister() and disable() methods. The first really deletes the task after termination while the second only disables its rescheduling. You can still use it afterwards. Right now the implementation of proceed()

def proceed(self):
    """
    Should this task continue running? Should be called periodically 
    by long tasks to check if the system wants them to exit.
    Returns 1 if its OK to continue, 0 if it's time to quit.
    """
    return not( self._close.isSet() or (not self._handle._isRunning) )     

uses the _close Event variable, which was also available trough the close() method. Don't count on that in future versions, it will probably be removed. Just use proceed() instead (take a look at TaskKit/Tests/BasicTest.py). Another API change after version 0.5 of Webware was the removal of the close variable in run(). If you plan to make serious use of TaskKit it's better to take the newest CVS snapshot of Webware, otherwise you will have to delete all occurences of close afterwards. Another thing to remember about tasks is, that they know nothing about scheduling, how often they will run (periodically or just once) or if they are on hold. All this is managed by the task wrapper class TaskManager, which will be discussed shortly. Let's look at some more examples first.

Generating static pages

On a high trafic web site (a la slashdot) it's common practice to use semistatic page generation techniques. For example you can generate the entry page as a static page once per minute. During this time the content will not be completely accurate (e.g. the number of comments will certainly increase), but nobody really cares about that. The benefit is a dramatic reduction of database requests. For other pages (like older news with comments attached) it makes more sense to generate static versions on demand. This is the case when the discussion has come to an end, but somebody adds a comment afterwards and implicitely changes the page by this action. Generating a static version will happen very seldom after the "hot phase" when getting data directly out of the database is more appropriate. So you need a periodic task which checks if there are new "dead" stories (e.g. no comments for 2 days) and marks them with a flag for static generation on demand. It should be clear by now, that an integrated Webware scheduling mechnism is very useful for this kind of things and the better approach than external cron jobs. Let's look a litle bit closer at the static generation technique now. First of all we need a PageGenerator class. To keep the example simple we just write the actual date into a file. In real life you will assemble much more complex data into such static pages.

from TaskKit.Task import Task
from time import *

html = '''<html>
<head><title>%s</title></head>
<body bgcolor="white">
<h1>%s</h1>
</body>
</html>
'''

class PageGenerator(Task):
    def __init__(self, filename):
        Task.__init__(self)
        self._filename = filename
        
    def run(self):
        f = open(self._filename, 'w')
        now = asctime(localtime(time()))
        f.write( html % ('Static Page',  now) )
        f.close()

Scheduling

That was easy. Now it's time to schedule our task. In the following example you can see how this is accomplished with TaskKit. As a general recommendation you should put all your tasks in a separate folder (with an empty __init__.py file to make this folder a Python package). First of all we create a new Scheduler object, start it as a thread and add a periodic page generation object (of type PageGenerator) with the addPeriodicAction method (this will probably be changed in the near future to the more constitent name addPeriodicTask). The first parameter here is the first execution time (which can be in the future), the second is the period (in seconds), the third an instance of our task class and the last parameter is a unique task name which allows us to find the task later on (e.g. if we want to change the period or put the task on hold).

from TaskKit.Scheduler import Scheduler
from Tasks.PageGenerator import PageGenerator
from time import *

def main():
    scheduler = Scheduler()
    scheduler.start()
    scheduler.addPeriodicAction(time(), 5, PageGenerator('static.html'), 'PageGenerator')
    sleep(20)
    scheduler.stop()
    
if __name__=='__main__':
    main()      

When you fire up this example you will notice that the timing is not 100% accurate. The reason for this seems to be an imprecise wait() function in the Python threading module. Unfortunately this method in indispensible because we need to be able to wake up a sleeping scheduler when scheduling new tasks with first execution times smaller than scheduler.nextTime(). This is achieved through the notify() method, which sets the notifyEvent (scheduler._notifyEvent.set()). On Unix we could use sleep and a signal to interrupt this system call, but TaskKit has to be plattform independant to be of any use. But don't worry, this impreciseness is not important for normal usage, because we are talking about scheduling in the minute (not second) range here. Unix cron jobs have a granularity of one minute, which is a good value for TaskKit too. Of course nobody can stop you starting tasks with a period of one second (but you have been warned that this is not a good idea, except for testing purposes).

Generating static pages again

Let's refine our example a little bit and plug it into Webware. We will write a Python servlet which loks like this:

every seconds
Task List
Task NamePeriod
SessionSweeper360
PageGenerator for static3.html30
PageGenerator for static1.html60
PageGenerator for static2.html120

When you click on the Generate button a new periodic PageGenerator task will be added to the Webware scheduler. Remember that this will generate a static page static.html every 60 seconds (if you use the default values). The new task name is "PageGenerator for filename", so you can use this servlet to change the settings of already scheduled tasks (by rescheduling) or add new PageGenerator tasks with different filenames. This is quite useless here, but as soon as you begin to parametrize your Task classes this approach can become quite powerful (consider for example a mail reminder form or collecting news from different news channels as periodic tasks with user defined parameters). In any case, don't be shy and contribute other interesting examples (the sky's the limit!).

Finally we come to the servlet code, which should be more or less self explanatory, except for the _action_ construct which is very well explained in the Webware documentation though (just in case you forgot that). app.taskManager() gives you the WebKit scheduler, which can be used to add new tasks. In real life you will have to make the scheduling information persistent and reschedule all tasks after a WebKit restart because it would be quite annoying to enter this data again and again. PersistantScheduler is a class which is on the ToDo list for the next TaskKit version and will probably be implemented with the new MiddleKit from Chuck Esterbrook. MiddleKit is a new object relational mapping framework for Python and greatly simplyfies this kind of developments. You'll certainly read more about it in the future.

import os, string, time
from ExamplePage import ExamplePage
from Tasks.PageGenerator import PageGenerator

class Schedule(ExamplePage):

    def writeContent(self):
        wr = self.write
        wr('<center><form method="post">')
        wr('<input type=submit name=_action_ value=Generate> ')
        wr('<input type=text name=filename value="static.html" size=20> every ') 
        wr('<input type=text name=seconds value=60 size=5> seconds')
        wr('</form>')
        wr('<table width=50% border=1 cellspacing=0>')
        wr('<tr bgcolor=00008B><th colspan=2><font color=white>Task List</font></th></tr>')
        wr('<tr bgcolor=#dddddd><td><b>Task Name</b></td><td><b>Period</b></td></tr>')
        for taskname, handler in self.application().taskManager().scheduledTasks().items():
            wr('<tr><td>%s</td><td>%s</td></tr>' % (taskname, handler.period()))
        wr('</table></center>')

    def generate(self, trans):
        app = self.application()
        tm = app.taskManager()
        req = self.request()
        if req.hasField('filename') and req.hasField('seconds'):
            self._filename = req.field('filename')
            self._seconds = string.atoi(req.field('seconds'))
            task = PageGenerator(app.serverSidePath('Examples/' + self._filename))
            taskname = 'PageGenerator for ' + self._filename
            tm.addPeriodicAction(time.time(), self._seconds, task, taskname)    
        self.writeBody()
                                    
    def methodNameForAction(self, name):
        return string.lower(name)

    def actions(self):
        return ExamplePage.actions(self) + ['generate']      

The Scheduler

Now it's time to take a closer look at the Scheduler class itself. As you have seen in the examples above, writing tasks is only a matter of overloading the run() method in a derived class and adding it to the scheduler with addTimedAction, addActionOnDemand, addDailyAction or addPeriodicAction. The scheduler will wrap the Task in a TaskHandler structure which knows all the scheduling details and add it to its _scheduled or _onDemand dictionaries. The latter is populated by addActionOnDemand and contains tasks which can be called any time by scheduler.runTaskNow('taskname') as you can see in the following example. After that the task has gone.

scheduler = Scheduler()
scheduler.start()
scheduler.addActionOnDemand(SimpleTask(), 'SimpleTask')
sleep(5)
print "Demanding SimpleTask"
scheduler.runTaskNow('SimpleTask')
sleep(5)
scheduler.stop()

If you need a task more than one time it's better to start it regularly with one of the add*Action methods first. It will be added to the _scheduled dictionary. If you do not need the task for a certain time disable it with scheduler.disableTask('taskname') and enable it later with scheduler.enableTask('taskname'). There are some more methods (e.g. demandTask(), stopTask(), ...) in the Scheduler class which are all documented by doc strings. Take a look at them and write your own examples to understand the methods (and maybe find bugs ;-)).

When a periodic task is scheduled it is added in a wrapped version to the _scheduled dictionary first. The (most of the time sleeping) scheduler thread always knows when to wake up and start the next task whose wrapper is moved to the _runnning dictionary. After completion of the task thread the handle reschedules the task by putting it back from _running to _scheduled), calculating the next execution time nextTime and possibly waking up the scheduler. It is important to know that you can manipulate the handle while the task is running, eg. change the period or call runOnCompletion to request that a task be re-run after its current completion. For normal use you will probably not need the handles at all, but the more you want to manipulate the task execution, the more you will appreciate the TaskHandler API. You get all the available handles from the scheduler with the running('taskname), scheduled('taskname') and onDemand('taskname') methods.

In our last example which was contributed by Jay Love, who debugged, stress tested and contributed a lot of refinements to TaskKit, you see how to write a period modifying Task. This is quite weird but shows the power of handle manipulations. The last thing to remember is, that the scheduler does not start a separate thread for each periodic task. It uses a thread for each task run instead and at any time keeps the number of threads as small as possible.

class SimpleTask(Task):

    def run(self):
        if self.proceed():
            print self.name(), time()
            print "Increasing period"
            self.handle().setPeriod(self.handle().period()+2)
        else:
            print "Should not proceed", self.name()

As you can see the TaskKit framework is quite sophisticated and will hopefully be used by many people from the Python community. If you have further question, please feel free to ask them on the Webware mailing list. (last changes: 2. March 2001)

Info

[1] Webware: http://webware.sourceforge.net/
[2] Ganymede: http://www.arlut.utexas.edu/gash2/

Published under the GNU Free Documentation License. PasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-0.1.1.html0000664000175000017500000000132410516756014024251 0ustar ianbianb TaskKit 0.1.1 Release Notes

TaskKit 0.1.1 Release Notes

Release Notes

  • Version 0.1.1 was released on 3/@@/2001.

This release is mainly a cleanup of some aspects of the code. The following changes affect the external interface.

  • Removed the close parameter from TaskKit.run(). This parameter was unnecessary. To check if the task should exit, call Task.proceed(), where a non-zero return value indicates that the task should proceed.
  • Various other cleanups. None affecting the core functionality.
PasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-0.7.html0000664000175000017500000000062410516756014024122 0ustar ianbianb TaskKit 0.7 Release Notes

TaskKit 0.7 Release Notes

Release Notes

Changes

  • New versioning strategy: all Webware components will now have their version numbers updated in lock-step.


PasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-0.8.html0000664000175000017500000000062410516756014024123 0ustar ianbianb TaskKit 0.8 Release Notes

TaskKit 0.8 Release Notes

Release Notes

Changes

  • New versioning strategy: all Webware components will now have their version numbers updated in lock-step.


PasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-X.Y.html0000664000175000017500000000206210516756014024232 0ustar ianbianb CGIWrapper Release Notes

CGIWrapper Release Notes

Version X.Y
Webware for Python X.Y Released on @@/@@/@@ Introduction
  • *

Major Changes

  • *

New Features

  • *

Improvements and Refinements

  • *

Security

  • *

Minor API Changes

  • *

Bugfixes

  • *


PasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/Docs/StyleSheet.css0000664000175000017500000000323010516756014023776 0ustar ianbianb PasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/Docs/TODO-TaskKit.text0000664000175000017500000000130210516756014024214 0ustar ianbianbTaskKit Webware for Python TO DO [ ] Test WebKit with the new changes [ ] Task.proceed() questionable name. run() runs and proceed() proceeds, er, no, it inquires? Maybe shouldProceed()? [ ] Does a Task's run() really always have to invoke self.proceed()? [ ] make tests that cover all methods and features [ ] Put this somewhere: # Based on code from Jonathan Abbey, jonabbey@arlut.utexas.edu # from the Ganymede Directory Management System # Python port and enhancements by Tom.Schwaller@linux-community.de [ ] Doc strings for all the major methods. [ ] User's Guide [ ] Properties: Better synopsis PasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/Docs/UsersGuide.html0000664000175000017500000000340210516756014024141 0ustar ianbianb TaskKit User's Guide

TaskKit User's Guide

Version X.Y
Webware for Python X.Y

Synopsis

TaskKit provides a framework for the scheduling and management of tasks which can be triggered periodically or at specific times. Tasks can also be forced to execute immediately, set on hold or rescheduled with a different period (even dynamically).

Feedback

You can e-mail webware-discuss@lists.sourceforge.net to give feedback, discuss features and get help using TaskKit.

Introduction

Overview

Task Kit is brand new and does not have a real User's Guide yet. However, there are doc strings in the source code. Also, if you search WebKit for "task" you will find a real world use of this kit.

The development status of TaskKit is alpha. You can expect API and usage changes in the next version. If you use TaskKit, we highly recommend that you stay in sync with the latest in-development Webware via CVS. You should then re-read the latest docs, including this User's Guide and the TO DO list.

Known Bugs

Known bugs and future work in general, are documented in TO DO.

Credit

Authors: Tom Schwaller, Jay Love


PasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/Docs/index.html0000664000175000017500000000534710516756014023203 0ustar ianbianb TaskKit Documentation
TaskKit Documentation

Name: TaskKit Summary:
Version: X.Y TaskKit provides a framework for the scheduling and management of tasks which can be triggered periodically or at specific times.
Status: beta
Py ver: 2.0
Webware: X.Y

Manuals: Source: Release Notes:
Quick Start
User's Guide
To Do
Class hierarchy
Class list
X.Y
0.8
0.7
0.1.1


Webware: Home   @ SourceForge PasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/Tests/0000775000175000017500000000000010516756100021403 5ustar ianbianbPasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/Tests/BasicTest.py0000664000175000017500000000275010516756014023646 0ustar ianbianbimport os, sys sys.path.insert(1, os.path.abspath('../..')) import TaskKit from TaskKit.Scheduler import Scheduler from TaskKit.Task import Task from time import time, sleep class SimpleTask(Task): def run(self): if self.proceed(): print self.name(), time() ## print "Increasing period" ## self.handle().setPeriod(self.handle().period()+2) else: print "Should not proceed", self.name() print "proceed for %s=%s, isRunning=%s" % (self.name(), self.proceed(), self._handle._isRunning) class LongTask(Task): def run(self): while 1: sleep(2) print "proceed for %s=%s, isRunning=%s" % (self.name(), self.proceed(), self._handle._isRunning) if self.proceed(): print ">>",self.name(), time() else: print "Should not proceed:", self.name() return def main(): from time import localtime scheduler = Scheduler() scheduler.start() scheduler.addPeriodicAction(time(), 1, SimpleTask(), 'SimpleTask1') scheduler.addTimedAction(time()+3, SimpleTask(), 'SimpleTask2') scheduler.addActionOnDemand(LongTask(), 'LongTask') scheduler.addDailyAction(localtime(time())[3], localtime(time())[4]+1, SimpleTask(), "DailyTask") sleep(5) print "Demanding LongTask" scheduler.runTaskNow('LongTask') sleep(1) # print "Stopping LongTask" # scheduler.stopTask("LongTask") sleep(2) # print "Deleting 'SimpleTask1'" # scheduler.unregisterTask("SimpleTask1") sleep(4) print "Calling stop" scheduler.stop() ## sleep(2) print "Test Complete" if __name__=='__main__': main() PasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/Tests/Test.py0000664000175000017500000000120610516756014022677 0ustar ianbianbimport os, sys sys.path.insert(1, os.path.abspath('../..')) import TaskKit #from MiscUtils import unittest import unittest class TaskKitTest(unittest.TestCase): def setUp(self): from TaskKit.Scheduler import Scheduler self.scheduler = Scheduler() def checkBasics(self): sched = self.scheduler sched.start() def tearDown(self): self.scheduler.stop() self.scheduler = None def makeTestSuite(): suite1 = unittest.makeSuite(TaskKitTest, 'check') return unittest.TestSuite((suite1,)) if __name__=='__main__': runner = unittest.TextTestRunner(stream=sys.stdout) unittest.main(defaultTest='makeTestSuite', testRunner=runner) PasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/Tests/__init__.py0000664000175000017500000000000210516756014023510 0ustar ianbianb# PasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/Properties.py0000664000175000017500000000063610516756014023020 0ustar ianbianbname = 'TaskKit' version = ('X', 'Y', 0) docs = [ {'name': "Quick Start", 'file': 'QuickStart.html'}, {'name': "User's Guide", 'file': 'UsersGuide.html'}, {'name': 'To Do', 'file': 'TODO-TaskKit.text'}, ] status = 'beta' requiredPyVersion = (2, 0, 0) synopsis = """TaskKit provides a framework for the scheduling and management of tasks which can be triggered periodically or at specific times.""" PasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/Scheduler.py0000664000175000017500000003106410516756014022601 0ustar ianbianb """ This is the TaskManager python package. It provides a system for running any number of predefined tasks in separate threads in an organized and controlled manner. A task in this package is a class derived from the Task class. The task should have a run method that, when called, performs some task. The Scheduler class is the organizing object. It manages the addition, execution, deletion, and well being of a number of tasks. Once you have created your task class, you call the Scheduler to get it added to the tasks to be run. """ from threading import Thread, Event from TaskHandler import TaskHandler from time import time, sleep from exceptions import IOError class Scheduler(Thread): """ The top level class of the TaskManager system. The Scheduler is a thread that handles organizing and running tasks. The Sheduler class should be instantiated to start a TaskManager sessions. It's run method should be called to start the TaskManager. It's stop method should be called to end the TaskManager session. """ ## Init ## def __init__(self, daemon=1): Thread.__init__(self) self._notifyEvent = Event() self._nextTime = None self._scheduled = {} self._running = {} self._onDemand = {} self._isRunning = 0 if daemon: self.setDaemon(1) ## Event Methods ## def wait(self, seconds=None): """ Our own version of wait. When called, it waits for the specified number of seconds, or until it is notified that it needs to wake up, through the notify event. """ try: self._notifyEvent.wait(seconds) except IOError, e: pass self._notifyEvent.clear() ## Attributes ## def runningTasks(self): return self._running def running(self, name, default=None): """ Returns a task with the given name from the "running" list, if it is present there. """ return self._running.get(name, default) def hasRunning(self, name): """ Check to see if a task with the given name is currently running. """ return self._running.has_key(name) def setRunning(self, handle): """ Add a task to the running dictionary. Used internally only. """ self._running[handle.name()] = handle def delRunning(self, name): """ Delete a task from the running list. Used Internally. """ try: handle = self._running[name] del self._running[name] return handle except: return None def scheduledTasks(self): return self._scheduled def scheduled(self, name, default=None): """ Returns a task from the scheduled list. """ return self._scheduled.get(name, default) def hasScheduled(self, name): """ Is the task with he given name in the scheduled list? """ return self._scheduled.has_key(name) def setScheduled(self, handle): """ Add the given task to the scheduled list. """ self._scheduled[handle.name()] = handle def delScheduled(self, name): """ Deleted a task with the given name from the scheduled list. """ try: handle = self._scheduled[name] del self._scheduled[name] return handle except: return None def onDemandTasks(self): return self._onDemand def onDemand(self, name, default=None): """ Returns a task from the onDemand list. """ return self._onDemand.get(name, default) def hasOnDemand(self, name): """ Is the task with he given name in the onDemand list? """ return self._onDemand.has_key(name) def setOnDemand(self, handle): """ Add the given task to the onDemand list. """ self._onDemand[handle.name()] = handle def delOnDemand(self, name): """ Deleted a task with the given name from the onDemand list. """ try: handle = self._onDemand[name] del self._onDemand[name] return handle except: return None def nextTime(self): return self._nextTime def setNextTime(self, time): self._nextTime = time def isRunning(self): return self._isRunning ## Adding Tasks ## def addTimedAction(self, time, task, name): """ This method is used to add an action to be run once, at a specific time. """ handle = self.unregisterTask(name) if not handle: handle = TaskHandler(self, time, 0, task, name) else: handle.reset(time, 0, task, 1) self.scheduleTask(handle) def addActionOnDemand(self, task, name): """ This method is used to add a task to the scheduler that will not be scheduled until specifically requested. """ handle = self.unregisterTask(name) if not handle: handle = TaskHandler(self, time(), 0, task, name) else: handle.reset(time(), 0, task, 1) self.setOnDemand(handle) def addDailyAction(self, hour, minute, task, name): """ This method is used to add an action to be run every day at a specific time. If a task with the given name is already registered with the scheduler, that task will be removed from the scheduling queue and registered anew as a periodic task. Can we make this addCalendarAction? What if we want to run something once a week? We probably don't need that for Webware, but this is a more generally useful module. This could be a difficult function, though. Particularly without mxDateTime. """ import time current = time.localtime(time.time()) currHour = current[3] currMin = current[4] if hour > currHour: hourDifference = hour - currHour if minute > currMin: minuteDifference = minute - currMin elif minute < currMin: minuteDifference = 60 - currMin + minute hourDifference -= 1 else: minuteDifference = 0 elif hour < currHour: hourDifference = 24 - currHour + hour if minute > currMin: minuteDifference = minute - currMin elif minute < currMin: minuteDifference = 60 - currMin + minute hourDifference -= 1 else: minuteDifference = 0 else: if minute > currMin: hourDifference = 0 minuteDifference = minute - currMin elif minute < currMin: minuteDifference = 60 - currMin + minute hourDifference = 23 else: hourDifference = 0 minuteDifference = 0 delay = (minuteDifference + (hourDifference * 60)) * 60 self.addPeriodicAction(time.time()+delay, 24*60*60, task, name) def addPeriodicAction(self, start, period, task, name): """ This method is used to add an action to be run at a specific initial time, and every period thereafter. The scheduler will not reschedule a task until the last scheduled instance of the task has completed. If a task with the given name is already registered with the scheduler, that task will be removed from the scheduling queue and registered anew as a periodic task. """ handle = self.unregisterTask(name) if not handle: handle = TaskHandler(self, start, period, task, name) else: handle.reset(start, period, task, 1) self.scheduleTask(handle) ## Task methods ## def unregisterTask(self, name): """ This method unregisters the named task so that it can be rescheduled with different parameters, or simply removed. """ handle = None if self.hasScheduled(name): handle = self.delScheduled(name) if self.hasOnDemand(name): handle = self.delOnDemand(name) if handle: handle.unregister() return handle def runTaskNow(self, name): """ This method is provided to allow a registered task to be immediately executed. Returns 1 if the task is either currently running or was started, or 0 if the task could not be found in the list of currently registered tasks. """ if self.hasRunning(name): return 1 handle = self.scheduled(name) if not handle: handle = self.onDemand(name) if not handle: return 0 self.runTask(handle) return 1 def demandTask(self, name): """ This method is provided to allow the server to request that a task listed as being registered on-demand be run as soon as possible. If the task is currently running, it will be flagged to run again as soon as the current run completes. Returns 0 if the task name could not be found on the on-demand or currently running lists. """ if not self.hasRunning(name) and not self.hasOnDemand(name): return 0 else: handle = self.running(name) if handle: handle.runOnCompletion() return 1 handle = self.onDemand(name) if not handle: return 0 self.runTask(handle) return 1 def stopTask(self, name): """ This method is provided to put an immediate halt to a running background task. Returns 1 if the task was either not running, or was running and was told to stop. """ handle = self.running(name) if not handle: return 0 handle.stop() return 1 def stopAllTasks(self): """ Terminate all running tasks. """ for i in self._running.keys(): print "Stopping ",i self.stopTask(i) def disableTask(self, name): """ This method is provided to specify that a task be suspended. Suspended tasks will not be scheduled until later enabled. If the task is currently running, it will not be interfered with, but the task will not be scheduled for execution in future until re-enabled. Returns 1 if the task was found and disabled. """ handle = self.running(name) if not handle: handle = self.scheduled(name) if not handle: return 0 handle.disable() return 1 def enableTask(self, name): """ This method is provided to specify that a task be re-enabled after a suspension. A re-enabled task will be scheduled for execution according to its original schedule, with any runtimes that would have been issued during the time the task was suspended simply skipped. Returns 1 if the task was found and enabled """ handle = self.running(name) if not handle: handle = self.scheduled(name) if not handle: return 0 handle.enable() return 1 def runTask(self, handle): """ This method is used by the Scheduler thread's main loop to put a task in the scheduled hash onto the run hash. """ name = handle.name() if self.delScheduled(name) or self.delOnDemand(name): self.setRunning(handle) handle.runTask() def scheduleTask(self, handle): """ This method takes a task that needs to be scheduled and adds it to the scheduler. All scheduling additions or changes are handled by this method. This is the only Scheduler method that can notify the run() method that it may need to wake up early to handle a newly registered task. """ self.setScheduled(handle) if not self.nextTime() or handle.startTime() < self.nextTime(): self.setNextTime(handle.startTime()) self.notify() ## Misc Methods ## def notifyCompletion(self, handle): """ This method is used by instances of TaskHandler to let the Scheduler thread know when their tasks have run to completion. This method is responsible for rescheduling the task if it is a periodic task. """ name = handle.name() if self.hasRunning(name): self.delRunning(name) if handle.startTime() and handle.startTime() > time(): self.scheduleTask(handle) else: if handle.reschedule(): self.scheduleTask(handle) elif not handle.startTime(): self.setOnDemand(handle) if handle.runAgain(): self.runTask(handle) def notify(self): self._notifyEvent.set() def stop(self): """ This method terminates the scheduler and its associated tasks. """ self._isRunning = 0 self.notify() self.stopAllTasks() self.join() # jdh: wait until the scheduler thread exits; otherwise # it's possible for the interpreter to exit before this thread # has a chance to shut down completely, which causes a traceback ## Main Method ## def run(self): """ This method is responsible for carrying out the scheduling work of this class on a background thread. The basic logic is to wait until the next action is due to run, move the task from our scheduled list to our running list, and run it. Other synchronized methods such as runTask(), scheduleTask(), and notifyCompletion(), may be called while this method is waiting for something to happen. These methods modify the data structures that run() uses to determine its scheduling needs. """ self._isRunning = 1 while 1: if not self._isRunning: return if not self.nextTime(): self.wait() else: nextTime = self.nextTime() currentTime = time() if currentTime < nextTime: sleepTime = nextTime - currentTime self.wait(sleepTime) if not self._isRunning: return currentTime = time() if currentTime >= nextTime: toRun = [] nextRun = None for handle in self._scheduled.values(): startTime = handle.startTime() if startTime <= currentTime: toRun.append(handle) else: if not nextRun: nextRun = startTime elif startTime < nextRun: nextRun = startTime self.setNextTime(nextRun) for handle in toRun: self.runTask(handle) PasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/Task.py0000664000175000017500000000265310516756014021567 0ustar ianbianbfrom MiscUtils import AbstractError class Task: def __init__(self): """ Subclasses should invoke super for this method. """ # Nothing for now, but we might do something in the future. pass def run(self): """ Override this method for you own tasks. Long running tasks can periodically use the proceed() method to check if a task should stop. """ raise AbstractError, self.__class__ ## Utility method ## def proceed(self): """ Should this task continue running? Should be called periodically by long tasks to check if the system wants them to exit. Returns 1 if its OK to continue, 0 if its time to quit """ return self._handle._isRunning ## Attributes ## def handle(self): """ A task is scheduled by wrapping a handler around it. It knows everything about the scheduling (periodicity and the like). Under normal circumstances you should not need the handler, but if you want to write period modifying run() methods, it is useful to have access to the handler. Use it with care. """ return self._handle def name(self): """ Returns the unique name under which the task was scheduled. """ return self._name ## Private method ## def _run(self, handle): """ This is the actual run method for the Task thread. It is a private method which should not be overriden. """ self._name = handle.name() self._handle = handle self.run() handle.notifyCompletion() PasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/TaskHandler.py0000664000175000017500000000611710516756014023064 0ustar ianbianb from time import time, sleep from threading import Event, Thread class TaskHandler: """ While the Task class only knows what task to perform with the run()-method, the TaskHandler has all the knowledge about the periodicity of the task. Instances of this class are managed by the Scheduler in the scheduled, running and onDemand dictionaries. """ ## Init ## def __init__(self, scheduler, start, period, task, name): self._scheduler = scheduler self._task = task self._name = name self._thread = None self._isRunning = 0 self._suspend = 0 self._lastTime = None self._startTime = start self._registerTime = time() self._reregister = 1 self._rerun = 0 self._period = abs(period) ## Scheduling ## def reset(self, start, period, task, reregister): self._startTime = start self._period = abs(period) self._task = task self._reregister = reregister def runTask(self): """ Runs this task in a background thread. """ if self._suspend: self._scheduler.notifyCompletion(self) return self._rerun = 0 self._thread = Thread(None, self._task._run, self.name(), (self,)) self._isRunning = 1 self._thread.start() def reschedule(self): """ Method to determine whether this task should be rescheduled. Increments the startTime and returns true if this is a periodically executed task. """ if self._period == 0: return 0 else: if self._lastTime - self._startTime > self._period: #if the time taken to run the task exceeds the period self._startTime = self._lastTime + self._period else: self._startTime = self._startTime + self._period return 1 def notifyCompletion(self): self._isRunning = 0 self._lastTime = time() self._scheduler.notifyCompletion(self) ## Attributes ## def isRunning(self): return self._isRunning def runAgain(self): """ This method lets the Scheduler check to see whether this task should be re-run when it terminates """ return self._rerun def isOnDemand(self): """ Returns true if this task is not scheduled for periodic execution. """ return self._period == 1 def runOnCompletion(self): """ Method to request that this task be re-run after its current completion. Intended for on-demand tasks that are requested by the Scheduler while they are already running. """ self._rerun = 1 def unregister(self): """ Method to request that this task not be kept after its current completion. Used to remove a task from the scheduler """ self._reregister = 0 self._rerun = 0 def disable(self): """ Method to disable future invocations of this task. """ self._suspend = 1 def enable(self): """ Method to enable future invocations of this task. """ self._suspend = 0 def period(self): """ Returns the period of this task. """ return self._period def setPeriod(self, period): """ Mmethod to change the period for this task. """ self._period = period def stop(self): self._isRunning = 0 def name(self): return self._name def startTime(self, newTime=None): if newTime: self._startTime = newTime return self._startTime PasteWebKit-1.0/paste/webkit/FakeWebware/TaskKit/__init__.py0000664000175000017500000000005210516756014022413 0ustar ianbianb def InstallInWebKit(appserver): pass PasteWebKit-1.0/paste/webkit/FakeWebware/WebKit/0000775000175000017500000000000010516756100020114 5ustar ianbianbPasteWebKit-1.0/paste/webkit/FakeWebware/WebKit/Application.py0000664000175000017500000000005110516756014022731 0ustar ianbianbfrom paste.webkit.wkapplication import * PasteWebKit-1.0/paste/webkit/FakeWebware/WebKit/HTTPExceptions.py0000664000175000017500000000046110516756014023314 0ustar ianbianbfrom paste.httpexceptions import * class HTTPAuthenticationRequired(HTTPUnauthorized): def __init__(self, realm=None, message=None, headers=None): headers = headers or {} headers['WWW-Authenticate'] = 'Basic realm=%s' % realm HTTPUnathorized.__init__(self, message, headers) PasteWebKit-1.0/paste/webkit/FakeWebware/WebKit/HTTPServlet.py0000664000175000017500000000005710516756014022620 0ustar ianbianbfrom paste.webkit.wkservlet import HTTPServlet PasteWebKit-1.0/paste/webkit/FakeWebware/WebKit/Page.py0000664000175000017500000000005010516756014021341 0ustar ianbianbfrom paste.webkit.wkservlet import Page PasteWebKit-1.0/paste/webkit/FakeWebware/WebKit/__init__.py0000664000175000017500000000000210516756014022221 0ustar ianbianb# PasteWebKit-1.0/paste/webkit/FakeWebware/WebUtils/0000775000175000017500000000000010516756100020465 5ustar ianbianbPasteWebKit-1.0/paste/webkit/FakeWebware/WebUtils/Funcs.py0000664000175000017500000001110010516756015022113 0ustar ianbianb""" WebUtils.Funcs provides some basic functions that are useful in HTML and web development. You can safely import * from WebUtils.Funcs if you like. TO DO * Document the 'codes' arg of htmlEncode/Decode. """ import string htmlCodes = [ ['&', '&'], ['<', '<'], ['>', '>'], ['"', '"'], # ['\n', '
'] ] htmlCodesReversed = htmlCodes[:] htmlCodesReversed.reverse() def htmlEncode(s, codes=htmlCodes): """ Returns the HTML encoded version of the given string. This is useful to display a plain ASCII text string on a web page.""" for code in codes: s = string.replace(s, code[0], code[1]) return s def htmlDecode(s, codes=htmlCodesReversed): """ Returns the ASCII decoded version of the given HTML string. This does NOT remove normal HTML tags like

. It is the inverse of htmlEncode(). """ for code in codes: s = string.replace(s, code[1], code[0]) return s _urlEncode = {} for i in range(256): _urlEncode[chr(i)] = '%%%02x' % i for c in string.letters + string.digits + '_,.-/': _urlEncode[c] = c _urlEncode[' '] = '+' def urlEncode(s): """ Returns the encoded version of the given string, safe for using as a URL. """ return string.join(map(lambda c: _urlEncode[c], list(s)), '') def urlDecode(s): """ Returns the decoded version of the given string. Note that invalid URLs will throw exceptons. For example, a URL whose % coding is incorrect. """ mychr = chr atoi = string.atoi parts = string.split(string.replace(s, '+', ' '), '%') for i in range(1, len(parts)): part = parts[i] parts[i] = mychr(atoi(part[:2], 16)) + part[2:] return string.join(parts, '') def htmlForDict(dict, addSpace=None, filterValueCallBack=None, maxValueLength=None): """ Returns an HTML string with a where each row is a key-value pair. """ keys = dict.keys() keys.sort() # A really great (er, bad) example of hardcoding. :-) html = ['
'] for key in keys: value = dict[key] if addSpace!=None and addSpace.has_key(key): target = addSpace[key] value = string.join(string.split(value, target), '%s '%target) if filterValueCallBack: value = filterValueCallBack(value, key, dict) value = str(value) if maxValueLength and len(value) > maxValueLength: value = value[:maxValueLength] + '...' html.append('\n' % (htmlEncode(str(key)), htmlEncode(value))) html.append('
%s %s  
') return string.join(html, '') def requestURI(dict): """ Returns the request URI for a given CGI-style dictionary. Uses REQUEST_URI if available, otherwise constructs and returns it from SCRIPT_NAME, PATH_INFO and QUERY_STRING. """ uri = dict.get('REQUEST_URI', None) if uri==None: uri = dict.get('SCRIPT_NAME', '') + dict.get('PATH_INFO', '') query = dict.get('QUERY_STRING', '') if query!='': uri = uri + '?' + query return uri def normURL(path): """Normalizes a URL path, like os.path.normpath, but acts on a URL independant of operating system environmant. """ if not path: return initialslash = path[0] == '/' lastslash = path[-1] == '/' comps = string.split(path, '/') newcomps = [] for comp in comps: if comp in ('','.'): continue if comp != '..': newcomps.append(comp) elif newcomps: newcomps.pop() path = string.join(newcomps, '/') if path and lastslash: path = path + '/' if initialslash: path = '/' + path return path ### Deprecated HTMLCodes = htmlCodes HTMLCodesReversed = htmlCodesReversed def HTMLEncode(s): print 'DEPRECATED: WebUtils.Funcs.HTMLEncode() on 02/24/01 in ver 0.3. Use htmlEncode() instead.' return htmlEncode(s) def HTMLDecode(s): print 'DEPRECATED: WebUtils.Funcs.HTMLDecode() on 02/24/01 in ver 0.3. Use htmlDecode() instead.' return htmlDecode(s) def URLEncode(s): print 'DEPRECATED: WebUtils.Funcs.URLEncode() on 02/24/01 in ver 0.3. Use urlEncode() instead.' return urlEncode(s) def URLDecode(s): print 'DEPRECATED: WebUtils.Funcs.URLDecode() on 02/24/01 in ver 0.3. Use urlDecode() instead.' return urlDecode(s) def HTMLForDictionary(dict, addSpace=None): print 'DEPRECATED: WebUtils.Funcs.HTMLForDictionary() on 02/24/01 in ver 0.3. Use htmlForDict() instead.' return htmlForDict(dict, addSpace) def RequestURI(dict): print 'DEPRECATED: WebUtils.Funcs.RequestURI() on 02/24/01 in ver 0.3. Use requestURI() instead.' return requestURI(dict) PasteWebKit-1.0/paste/webkit/FakeWebware/WebUtils/__init__.py0000664000175000017500000000000210516756015022573 0ustar ianbianb# PasteWebKit-1.0/paste/webkit/FakeWebware/README.txt0000664000175000017500000000050410516756015020431 0ustar ianbianbThis directory can be added to sys.path so that all your Webware imports will still work, but will import WSGIWebKit versions of the objects. Items will be added here on an as-needed basis. I don't want to bring every public object from Webware into this setup; in part because many of them are not currently implemented. PasteWebKit-1.0/paste/webkit/FakeWebware/__init__.py0000664000175000017500000000000210516756015021035 0ustar ianbianb# PasteWebKit-1.0/paste/webkit/examples/0000775000175000017500000000000010516756100016362 5ustar ianbianbPasteWebKit-1.0/paste/webkit/examples/EchoServlet.py0000664000175000017500000000306510516756015021170 0ustar ianbianbr"""\ Paste/WebKit application Does things as requested. Takes variables: header.header-name=value, like header.location=http://yahoo.com error=code, like error=301 (temporary redirect) error=assert (assertion error) environ=true, display all the environmental variables, like key=str(value)\n message=string display string """ # Special WSGI version of WebKit: from paste.webkit.wkservlet import Page from paste import httpexceptions class EchoServlet(Page): def writeHTML(self): req = self.request() headers = {} for key, value in req.fields().items(): if key.startswith('header.'): name = key[len('header.'):] self.response().setHeader(name, value) # @@: I shouldn't have to do this: headers[name] = value error = req.field('error', None) if error and error != 'iter': if error == 'assert': assert 0, "I am asserting zero!" raise httpexceptions.get_exception( int(error), headers=headers) if req.field('environ', None): items = req.environ().items() items.sort() self.response().setHeader('content-type', 'text/plain') for name, value in items: self.write('%s=%s\n' % (name, value)) return if req.hasField('message'): self.response().setHeader('content-type', 'text/plain') self.write(req.field('message')) return self.write('hello world!') PasteWebKit-1.0/paste/webkit/examples/__init__.py0000664000175000017500000000000210516756015020470 0ustar ianbianb# PasteWebKit-1.0/paste/webkit/paster_templates/0000775000175000017500000000000010516756100020120 5ustar ianbianbPasteWebKit-1.0/paste/webkit/paster_templates/webkit/0000775000175000017500000000000010516756100021405 5ustar ianbianbPasteWebKit-1.0/paste/webkit/paster_templates/webkit/+package+/0000775000175000017500000000000010516756100023126 5ustar ianbianbPasteWebKit-1.0/paste/webkit/paster_templates/webkit/+package+/web/0000775000175000017500000000000010516756100023703 5ustar ianbianbPasteWebKit-1.0/paste/webkit/paster_templates/webkit/+package+/web/__init__.py_tmpl0000664000175000017500000000035610516756015027061 0ustar ianbianbimport os from paste import wsgilib from ${package}.sitepage import CONFIG def urlparser_hook(environ): if not environ.has_key('${package}.base_href'): environ['${package}.base_href'] = environ.get('SCRIPT_NAME', '') or '/' PasteWebKit-1.0/paste/webkit/paster_templates/webkit/+package+/web/index.py_tmpl0000664000175000017500000000021610516756015026424 0ustar ianbianbfrom ${package}.sitepage import SitePage, CONFIG class index(SitePage): def setup(self): self.options.title = ${repr(project)} PasteWebKit-1.0/paste/webkit/paster_templates/webkit/+package+/sitepage.py_tmpl0000664000175000017500000000225010516756015026341 0ustar ianbianbimport os from Component import CPage from Component.notify import NotifyComponent from ZPTKit.zptcomponent import ZPTComponent from paste.deploy import CONFIG from paste.url import URL # -*- Extra imports: -*- __all__ = ['SitePage', 'CONFIG'] class SitePage(CPage): components = [ ZPTComponent([os.path.join(os.path.dirname(__file__), 'templates')]), NotifyComponent(), # -*- Extra components: -*- ] def title(self): return self.options.get('title', CPage.title(self)) def awake(self, trans): CPage.awake(self, trans) # Add application-wide setup routines here self.setup() def setup(self): # This method should only be overridden by servlets, not # abstract classes (also teardown()). pass def sleep(self, trans): self.teardown() CPage.sleep(self, trans) def teardown(self): pass def writeHTML(self): self.writeTemplate() def preAction(self, action): self.setView('writeContent') def postAction(self, action): if self.view() is not None: self.writeHTML() PasteWebKit-1.0/paste/webkit/paster_templates/webkit/+package+/wsgiapp.py_tmpl0000664000175000017500000000043710516756015026217 0ustar ianbianbimport os from paste.webkit import wsgiapp def make_app( global_conf, # Required configuration parameters (if any): **kw): return wsgiapp.make_webkit_app( global_conf, package_name='${package}', root_path=os.path.dirname(__file__), **kw) PasteWebKit-1.0/paste/webkit/paster_templates/webkit/+project+.egg-info/0000775000175000017500000000000010516756100024673 5ustar ianbianbPasteWebKit-1.0/paste/webkit/paster_templates/webkit/+project+.egg-info/sqlobject.txt_tmpl0000664000175000017500000000011010516756015030453 0ustar ianbianbdb_module=${package}.db history_dir=$$base/${package}/sqlobject-history PasteWebKit-1.0/paste/webkit/paster_templates/webkit/tests/0000775000175000017500000000000010516756100022547 5ustar ianbianbPasteWebKit-1.0/paste/webkit/paster_templates/webkit/tests/conftest.py_tmpl0000664000175000017500000000027610516756015026014 0ustar ianbianb# You can add extra py.test options here... import os import sys import pkg_resources sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) pkg_resources.require(${repr(project)}) PasteWebKit-1.0/paste/webkit/paster_templates/webkit/tests/fixture.py_tmpl0000664000175000017500000000057210516756015025654 0ustar ianbianbimport os from paste.deploy import loadapp from paste.fixture import TestApp here_dir = os.path.dirname(__file__) conf_dir = os.path.join(os.path.dirname(here_dir), 'docs') def setup_module(mod=None): wsgiapp = loadapp('config:devel_config.ini#test', relative_to=conf_dir) app = TestApp(wsgiapp) if mod: mod.app = app return app PasteWebKit-1.0/paste/webkit/paster_templates/blank_servlet.py_tmpl0000664000175000017500000000014510516756015024366 0ustar ianbianbfrom ${base_package}.sitepage import * class ${name}(SitePage): def setup(self): pass PasteWebKit-1.0/paste/webkit/__init__.py0000664000175000017500000000000210516756016016653 0ustar ianbianb# PasteWebKit-1.0/paste/webkit/servlet_script.py0000664000175000017500000001021210516756016020170 0ustar ianbianbimport os import glob from paste.script.command import Command, BadCommand from paste.script import pluginlib, copydir class ServletCommand(Command): summary = "Create servlet" usage = 'SERVLET_NAME' min_args = 1 max_args = 1 default_verbosity = 1 parser = Command.standard_parser(simulate=True, quiet=True, interactive=True, overwrite=True) parser.add_option('--no-servlet', action='store_true', dest='no_servlet', help="Don't create a servlet; just template(s)") group_name = 'Paste WebKit' def command(self): servlet = self.args[0] if servlet.endswith('.py'): # Erase extensions servlet = servlet[:-3] if '.' in servlet: # Turn into directory name: servlet = servlet.replace('.', os.path.sep) if '/' != os.path.sep: servlet = servlet.replace('/', os.path.sep) parts = servlet.split(os.path.sep) name = parts[-1] base_package, base = self.web_dir() if not parts[:-1]: dir = '' elif len(parts[:-1]) == 1: dir = parts[0] else: dir = os.path.join(*parts[:-1]) vars = {'name': name, 'base_package': base_package} if not self.options.no_servlet: self.create_servlet( base_package, base, dir, name, vars) template_base = os.path.join(os.path.dirname(base), 'templates') if not os.path.exists(template_base): if self.verbose > 1: print 'No template directory %s' % template_base return blanks = glob.glob(os.path.join(base, template_base, 'blank.*')) if not blanks and self.verbose: print 'No blank templates found in %s' % self.shorten(template_base) for blank in blanks: self.create_blank( blank, template_base, dir, name, vars) def create_servlet(self, base_package, base, dir, name, vars): self.ensure_dir(os.path.join(base, dir)) blank = os.path.join(base, 'blank.py') if not os.path.exists(blank): blank = os.path.join(os.path.dirname(__file__), 'paster_templates', 'blank_servlet.py_tmpl') f = open(blank, 'r') content = f.read() f.close() if blank.endswith('_tmpl'): content = copydir.substitute_content(content, vars, filename=blank) dest = os.path.join(base, dir, '%s.py' % name) self.ensure_file(dest, content) def create_blank(self, blank, template_base, dir, name, vars): ext = os.path.splitext(blank)[1] f = open(blank, 'r') content = f.read() f.close() if ext.endswith('_tmpl'): content = copydir.substitute_content( content, vars, filename=blank) ext = ext[:-5] dest = os.path.join(template_base, dir, name + ext) self.ensure_dir(os.path.dirname(dest)) self.ensure_file(dest, content) def web_dir(self): egg_info = pluginlib.find_egg_info_dir(os.getcwd()) # @@: Should give error about egg_info when top_leve.txt missing f = open(os.path.join(egg_info, 'top_level.txt')) packages = [l.strip() for l in f.readlines() if l.strip() and not l.strip().startswith('#')] f.close() # @@: This doesn't support deeper servlet directories, # or packages not kept at the top level. base = os.path.dirname(egg_info) possible = [] for pkg in packages: d = os.path.join(base, pkg, 'web') if os.path.exists(d): possible.append((pkg, d)) if not possible: raise BadCommand( "No web package found (looked in %s)" % ', '.join(packages)) if len(possible) > 1: raise BadCommand( "Multiple web packages found (%s)" % possible) return possible[0] PasteWebKit-1.0/paste/webkit/templates.py0000664000175000017500000000042010516756016017116 0ustar ianbianbimport os from paste.script.templates import Template class WebKit(Template): _template_dir = 'paster_templates/webkit' summary = "A Paste WebKit web application" egg_plugins = ['PasteWebKit'] required_templates = ['PasteDeploy#paste_deploy'] PasteWebKit-1.0/paste/webkit/wkapplication.py0000664000175000017500000000225510516756016017775 0ustar ianbianb""" A mostly dummy class to simulate the Webware Application object. """ from wkcommon import NoDefault import threading try: from TaskKit.Scheduler import Scheduler except ImportError: Scheduler = None _taskManager = None _makeTaskManagerLock = threading.Lock() def taskManager(): global _taskManager if _taskManager is None: if Scheduler is None: assert 0, ( "FakeWebware is not installed, and/or TaskKit is " "not available") _makeTaskManagerLock.acquire() try: if _taskManager is None: _taskManager = Scheduler(1) _taskManager.start() finally: _makeTaskManagerLock.release() return _taskManager class Application(object): def __init__(self, transaction): self._transaction = transaction def forward(self, trans, url, context=None): assert context is None, "Contexts are not supported" trans.forward(url) def setting(self, setting, default=NoDefault): assert default is not NoDefault, "No settings are defined" return default def taskManager(self): return taskManager() PasteWebKit-1.0/paste/webkit/wkcommon.py0000664000175000017500000001164310516756016016763 0ustar ianbianbimport cgi import urllib import warnings import inspect import Cookie as CookieEngine __all__ = ['NoDefault', 'htmlEncode', 'htmlDecode', 'urlEncode', 'urlDecode', ] try: from MiscUtils import NoDefault except ImportError: class NoDefault: pass def htmlEncode(s): return cgi.escape(s, 1) def htmlDecode(s): for char, code in [('&', '&'), ('<', '<'), ('>', '>'), ('"', '"')]: s = s.replace(code, char) return s urlDecode = urllib.unquote urlEncode = urllib.quote def requestURI(dict): """ Returns the request URI for a given CGI-style dictionary. Uses REQUEST_URI if available, otherwise constructs and returns it from SCRIPT_NAME, PATH_INFO and QUERY_STRING. """ uri = dict.get('REQUEST_URI', None) if uri is None: uri = dict.get('SCRIPT_NAME', '') + dict.get('PATH_INFO', '') query = dict.get('QUERY_STRING', '') if query: uri = uri + '?' + query return uri def deprecated(msg=None): # @@: Right now this takes up a surprising amount of CPU time # (blech! inspect is slow) return if not msg: frame = inspect.stack()[1] methodName = frame[3] msg = 'The use of %s is deprecated' % methodName warnings.warn(msg, DeprecationWarning, stacklevel=3) class Cookie: """ Cookie is used to create cookies that have additional attributes beyond their value. Note that web browsers don't typically send any information with the cookie other than it's value. Therefore `HTTPRequest.cookie` simply returns a value such as an integer or a string. When the server sends cookies back to the browser, it can send a cookie that simply has a value, or the cookie can be accompanied by various attributes (domain, path, max-age, ...) as described in `RFC 2109`_. Therefore, in HTTPResponse, `setCookie` can take either an instance of the Cookie class, as defined in this module, or a value. Note that Cookies values get pickled (see the `pickle` module), so you can set and get cookies that are integers, lists, dictionaries, etc. .. _`RFC 2109`: ftp://ftp.isi.edu/in-notes/rfc2109.txt """ ## Future ## ## * This class should provide error checking in the setFoo() ## methods. Or maybe our internal Cookie implementation ## already does that? ## * This implementation is probably not as efficient as it ## should be, [a] it works and [b] the interface is stable. ## We can optimize later. def __init__(self, name, value): """ Create a cookie -- properties other than `name` and `value` are set with methods. """ self._cookies = CookieEngine.SimpleCookie() self._name = name self._value = value self._cookies[name] = value self._cookie = self._cookies[name] """ **Accessors** """ def comment(self): return self._cookie['comment'] def domain(self): return self._cookie['domain'] def maxAge(self): return self._cookie['max-age'] def expires(self): return self._cookie['expires'] def name(self): return self._name def path(self): return self._cookie['path'] def isSecure(self): return self._cookie['secure'] def value(self): return self._value def version(self): return self._cookie['version'] """ **Setters** """ def setComment(self, comment): self._cookie['comment'] = comment def setDomain(self, domain): self._cookie['domain'] = domain def setExpires(self, expires): self._cookie['expires'] = expires def setMaxAge(self, maxAge): self._cookie['max-age'] = maxAge def setPath(self, path): self._cookie['path'] = path def setSecure(self, bool): self._cookie['secure'] = bool def setValue(self, value): self._value = value self._cookies[self._name] = value def setVersion(self, version): self._cookie['version'] = version """ **Misc** """ def delete(self): """ When sent, this should delete the cookie from the user's browser, by making it empty, expiring it in the past, and setting its max-age to 0. One of these will delete the cookie for any browser (which one actually works depends on the browser). """ self._value = '' self._cookie['expires'] = "Mon, 01-Jan-1900 00:00:00 GMT" self._cookie['max-age'] = 0 self._cookie['path'] = '/' def headerValue(self): """ Returns a string with the value that should be used in the HTTP headers. """ items = self._cookies.items() assert(len(items)==1) return items[0][1].OutputString() PasteWebKit-1.0/paste/webkit/wkrequest.py0000664000175000017500000002473110516756016017165 0ustar ianbianb""" A Webware HTTPRequest object, implemented based on the WSGI request environment dictionary. """ import time import traceback import cgi import sys, os from wkcommon import NoDefault, requestURI, deprecated from Cookie import SimpleCookie as Cookie class HTTPRequest(object): def __init__(self, transaction, environ): self._environ = environ self._transaction = transaction if environ.has_key('webkit.time'): self._time = environ['webkit.time'] else: self._time = time.time() self._input = environ['wsgi.input'] self._setupPath() self._setupFields() self._setupCookies() self._pathInfo = None self._serverRootPath = "" # @@: I'm leaving out automatic path sessions self._sessionExpired = False def _setupPath(self): self._environ['PATH_INFO'] = self._environ.get('PATH_INFO', '') if not self._environ.has_key('REQUEST_URI'): self._environ['REQUEST_URI'] = requestURI(self._environ) # @@: Not necessarily true for WSGI: self._adapterName = self._environ.get('SCRIPT_NAME') def _setupFields(self): self._environ.setdefault('QUERY_STRING', '') self._fieldStorage = cgi.FieldStorage( self._input, environ=self._environ, keep_blank_values=True, strict_parsing=False) try: keys = self._fieldStorage.keys() except TypeError: # Maybe an XML-RPC request keys = [] dict = {} for key in keys: value = self._fieldStorage[key] if not isinstance(value, list): if not value.filename: # Turn the MiniFieldStorage into a string: value = value.value else: value = [v.value for v in value] dict[key] = value if self._environ['REQUEST_METHOD'].upper() == 'POST': # Then we must also parse GET variables self._getFields = cgi.parse_qs( self._environ.get('QUERY_STRING', ''), keep_blank_values=True, strict_parsing=False) for name, value in self._getFields.items(): if not dict.has_key(name): if isinstance(value, list) and len(value) == 1: # parse_qs always returns a list of lists, # while FieldStorage only uses lists for # keys that actually repeat; this fixes that. value = value[0] dict[name] = value self._fields = dict def _setupCookies(self): cookies = Cookie() if self._environ.has_key('HTTP_COOKIE'): try: cookies.load(self._environ['HTTP_COOKIE']) except: traceback.print_exc(file=self._environ['wsgi.errors']) dict = {} for key in cookies.keys(): dict[key] = cookies[key].value self._cookies = dict def protocol(self): return 'HTTP/1.0' def time(self): return self._time def timeStamp(self): return time.asctime(time.localtime(self.time())) ## Transactions ## def transaction(self): return self._transaction def setTransaction(self, trans): self._transaction = trans ## Values ## def value(self, name, default=NoDefault): if self._fields.has_key(name): return self._fields[name] else: return self.cookie(name, default) def hasValue(self, name): return self._fields.has_key(name) or self._cookies.has_key(name) def extraURLPath(self): return self._environ.get('PATH_INFO', '') ## Fields ## def fieldStorage(self): return self._fieldStorage def field(self, name, default=NoDefault): if default is NoDefault: return self._fields[name] else: return self._fields.get(name, default) def hasField(self, name): return self._fields.has_key(name) def fields(self): return self._fields def setField(self, name, value): self._fields[name] = value def delField(self, name): del self._fields[name] ## Cookies ## def cookie(self, name, default=NoDefault): """ Returns the value of the specified cookie. """ if default is NoDefault: return self._cookies[name] else: return self._cookies.get(name, default) def hasCookie(self, name): return self._cookies.has_key(name) def cookies(self): """ Returns a dictionary-style object of all Cookie objects the client sent with this request.""" return self._cookies ## Variables passed by server ## def serverDictionary(self): """ Returns a dictionary with the data the web server gave us, like HTTP_HOST or HTTP_USER_AGENT. """ return self._environ ## Sessions ## def session(self): """ Returns the session associated with this request, either as specified by sessionId() or newly created. This is a convenience for transaction.session() """ return self._transaction.session() def isSessionExpired(self): """ Returns bool: whether or not this request originally contained an expired session ID. Only works if the Application.config setting "IgnoreInvalidSession" is set to 1; otherwise you get a canned error page on an invalid session, so your servlet never gets processed. """ return self._sessionExpired def setSessionExpired(self, sessionExpired): self._sessionExpired = sessionExpired ## Authentication ## def remoteUser(self): """ Always returns None since authentication is not yet supported. Take from CGI variable REMOTE_USER. """ # @@ 2000-03-26 ce: maybe belongs in section below. clean up docs return self._environ['REMOTE_USER'] ## Remote info ## def remoteAddress(self): """ Returns a string containing the Internet Protocol (IP) address of the client that sent the request. """ return self._environ['REMOTE_ADDR'] def remoteName(self): """ Returns the fully qualified name of the client that sent the request, or the IP address of the client if the name cannot be determined. """ env = self._environ return env.get('REMOTE_NAME', env['REMOTE_ADDR']) ## Path ## def urlPath(self): raise NotImplementedError def originalURLPath(self): environ = self._environ.get('recursive.previous_environ', self._environ) url = environ.get("SCRIPT_NAME", '') + environ.get('PATH_INFO', '') #self._environ['wsgi.errors'].write('Original URL: %r (from %r)\n' % (url, environ)) return url def urlPathDir(self): raise NotImplementedError def getstate(self): raise NotImplementedError def setURLPath(self, path): raise NotImplementedError def serverSidePath(self, path=None): raise NotImplementedError def serverSideContextPath(self, path=None): here = sys.modules[self.transaction().servlet().__class__.__module__].__file__ # @@: not correct for modules that are in subpackages of web base = os.path.dirname(here) if path: return os.path.join(base, path) else: return base def contextName(self): return '' def servletURI(self): """This is the URI of the servlet, without any query strings or extra path info""" # @@: should be implemented raise NotImplementedError def uriWebKitRoot(self): raise NotImplementedError def fsPath(self): raise NotImplementedError def serverURL(self): raise NotImplementedError def serverURLDir(self): raise NotImplementedError def siteRoot(self): raise NotImplementedError def siteRootFromCurrentServlet(self): raise NotImplementedError def servletPathFromSiteRoot(self): raise NotImplementedError ## Special ## def adapterName(self): """ Returns the name of the adapter as it appears in the URL. Example: '/WebKit.cgi' This is useful in special cases when you are constructing URLs. See Testing/Main.py for an example use. """ deprecated() return '/'.join(self._environ['SCRIPT_NAME'].split('/')[:-1]) def rawRequest(self): raise NotImplementedError def environ(self): return self._environ def rawInput(self, rewind=0): """ This gives you a file-like object for the data that was sent with the request (e.g., the body of a POST request, or the documented uploaded in a PUT request). The file might not be rewound to the beginning if there was valid, form-encoded POST data. Pass rewind=1 if you want to be sure you get the entire body of the request. """ fs = self.fieldStorage() if rewind: fs.file.seek(0) return fs.file ## Information ## # @@ 2000-05-10: See FUTURE section of class doc string def servletPath(self): raise NotImplementedError def contextPath(self): raise NotImplementedError def pathInfo(self): raise NotImplementedError def pathTranslated(self): raise NotImplementedError def queryString(self): """ Returns the query string portion of the URL for this request. Taken from the CGI variable QUERY_STRING. """ return self._environ.get('QUERY_STRING', '') def uri(self): """ Returns the request URI, which is the entire URL except for the query string. """ return self._environ['REQUEST_URI'] def method(self): """ Returns the HTTP request method (in all uppercase), typically from the set GET, POST, PUT, DELETE, OPTIONS and TRACE.""" return self._environ['REQUEST_METHOD'].upper() def sessionId(self): """ Returns a string with the session id specified by the client, or None if there isn't one. """ sid = self.value('_SID_', None) return sid def config(self): return self._environ.get('paste.config', {}) ## Inspection ## def info(self): raise NotImplementedError def htmlInfo(self): raise NotImplementedError PasteWebKit-1.0/paste/webkit/wkresponse.py0000664000175000017500000002135010516756016017325 0ustar ianbianb""" A Webware HTTPResponse object. """ import time from wkcommon import NoDefault, Cookie from MiscUtils.DateInterval import timeDecode import urlparse from paste.request import resolve_relative_url TimeTupleType = type(time.gmtime(0)) class HTTPResponse(object): def __init__(self, transaction, environ, start_response): self._transaction = transaction self._environ = environ self._start_response = start_response self._writer = None self._committed = False self._autoFlush = False self.reset() def endTime(self): return self._endTime def recordEndTime(self): """ Stores the current time as the end time of the response. This should be invoked at the end of deliver(). It may also be invoked by the application for those responses that never deliver due to an error.""" self._endTime = time.time() ## Headers ## def header(self, name, default=NoDefault): """ Returns the value of the specified header. """ if default is NoDefault: return self._headers[name.lower()] else: return self._headers.get(name.lower(), default) def hasHeader(self, name): return self._headers.has_key(name.lower()) def setHeader(self, name, value): """ Sets a specific header by name. """ assert self._committed==0, "Headers have already been sent" assert type(name) is str, ( "Header %r name is not string" % name) assert type(value) is str, ( "Header %s value is not string (%r)" % (name, value)) self._headers[name.lower()] = value def headers(self): """ Returns a dictionary-style object of all Header objects contained by this request. """ return self._headers def clearHeaders(self): """ Clears all the headers. You might consider a setHeader('Content-type', 'text/html') or something similar after this.""" assert self._committed==0 self._headers = {} ## Cookies ## def cookie(self, name): """ Returns the value of the specified cookie. """ return self._cookies[name] def hasCookie(self, name): """ Returns true if the specified cookie is present. """ return self._cookies.has_key(name) def setCookie(self, name, value, path='/', expires='ONCLOSE', secure=False): """ Set a cookie. You can also set the path (which defaults to /), You can also set when it expires. It can expire: 'NOW': this is the same as trying to delete it, but it doesn't really seem to work in IE 'ONCLOSE': the default behavior for cookies (expires when the browser closes) 'NEVER': some time in the far, far future. integer: a timestamp value tuple: a tuple, as created by the time module """ cookie = Cookie(name, value) if expires == 'ONCLOSE' or not expires: pass # this is already default behavior elif expires == 'NOW' or expires == 'NEVER': t = time.gmtime(time.time()) if expires == 'NEVER': t = (t[0] + 10,) + t[1:] t = time.strftime("%a, %d-%b-%Y %H:%M:%S GMT", t) cookie.setExpires(t) else: t = expires if isinstance(t, (str, unicode)) and t.startswith('+'): interval = timeDecode(t[1:]) t = time.time() + interval if isinstance(t, (int, long, float)): t = time.gmtime(t) if isinstance(t, (tuple, TimeTupleType)): t = time.strftime("%a, %d-%b-%Y %H:%M:%S GMT", t) cookie.setExpires(t) if path: cookie.setPath(path) if secure: cookie.setSecure(secure) self.addCookie(cookie) def addCookie(self, cookie): """ Adds a cookie that will be sent with this response. cookie is a Cookie object instance. See WebKit.Cookie. """ assert self._committed==0 assert isinstance(cookie, Cookie) self._cookies[cookie.name()] = cookie def delCookie(self, name): """ Deletes a cookie at the browser. To do so, one has to create and send to the browser a cookie with parameters that will cause the browser to delete it. """ if self._cookies.has_key(name): self._cookies[name].delete() else: cookie = Cookie(name, None) cookie.delete() self.addCookie(cookie) def cookies(self): """ Returns a dictionary-style object of all Cookie objects that will be sent with this response. """ return self._cookies def clearCookies(self): """ Clears all the cookies. """ assert self._committed==0 self._cookies = {} ## Status ## def setStatus(self, code, msg=''): """ Set the status code of the response, such as 200, 'OK'. """ assert self._committed==0, "Headers already sent." self.setHeader('Status', str(code) + ' ' + msg) ## Special responses ## def sendError(self, code, msg=''): """ Sets the status code to the specified code and message. """ assert self._committed==0, "Response already partially sent" self.setStatus(code, msg) def sendRedirect(self, url): """ This method sets the headers and content for the redirect, but does NOT change the cookies. Use clearCookies() as appropriate. @@ 2002-03-21 ce: I thought cookies were ignored by user agents if a redirect occurred. We should verify and update code or docs as appropriate. """ # ftp://ftp.isi.edu/in-notes/rfc2616.txt # Sections: 10.3.3 and others url = resolve_relative_url( url, self._transaction.request().environ()) assert not self._committed, "Headers already sent" self.setHeader('Status', '302 Redirect') self.setHeader('Location', url) self.setHeader('Content-type', 'text/html') self.write(' This page has been redirected to ' '%s. ' % (url, url)) ## Output ## def write(self, charstr=None): """ Write charstr to the response stream. """ if not charstr: return if self._autoFlush: assert self._committed self._writer(charstr) else: self._output.append(charstr) def flush(self, autoFlush=True): """ Send all accumulated response data now. Commits the response headers and tells the underlying stream to flush. if autoFlush is true, the responseStream will flush itself automatically from now on. """ if not self._committed: self.commit() if self._output: self._writer(''.join(self._output)) self._autoFlush = autoFlush def isCommitted(self): """ Has the reponse already been partially or completely sent? If this returns true, no new headers/cookies can be added to the response. """ return self._committed def deliver(self): """ The final step in the processing cycle. Not used for much with responseStreams added. """ self.recordEndTime() if not self._committed: self.commit() def commit(self): """ Write out all headers to the reponse stream, and tell the underlying response stream it can start sending data. """ status = self._headers['status'] del self._headers['status'] headers = self._headers.items() for cookie in self._cookies.values(): headers.append(('Set-Cookie', cookie.headerValue())) self._writer = self._start_response(status, headers) self._committed = True def wsgiIterator(self): return self._output def recordSession(self): raise NotImplementedError def reset(self): """ Resets the response (such as headers, cookies and contents). """ assert self._committed == 0 self._headers = {} self.setHeader('Content-type','text/html') self.setHeader('Status', '200 OK') self._cookies = {} self._output = [] def rawResponse(self): raise NotImplementedError def size(self): raise NotImplementedError def mergeTextHeaders(self, headerstr): raise NotImplementedError PasteWebKit-1.0/paste/webkit/wkservlet.py0000664000175000017500000003553210516756016017162 0ustar ianbianb""" This implements all of the Webware servlets (Servlet, HTTPServlet, and Page), as WSGI applications. The servlets themselves are applications, and __call__ is provided to do this. """ import wkcommon from wktransaction import Transaction from paste.util import classinstance class ServletSupplement(object): def __init__(self, servlet, trans): self.servlet = servlet self.trans = trans def extraData(self): result = {} result[('normal', 'Servlet variables')] = vars = {} hide = self.servlet.__traceback_supplement_hide_vars__ for name, value in self.servlet.__dict__.items(): if name in hide: continue vars[name] = value result[('extra', 'Form variables')] = form = {} fields = self.trans.request().fields() for name, value in fields.items(): value = str(value) if len(value) > 200: value = value[:200] + '...' form[name] = value if not form: form['none?'] = 'No fields submitted' return result class ReturnIterException(Exception): def __init__(self, app_iter): self.app_iter = app_iter class Servlet(object): # This is nested in Servlet so that transactions can access it as # an attribute, instead of having to import this module. (If they # had to import this module, there would be a circular import) # @@: Why not just put this in wktransaction? ReturnIterException = ReturnIterException def __call__(self, environ, start_response): """ The core WSGI method, and the core of the servlet execution. """ __traceback_hide__ = 'before_and_this' trans = Transaction(environ, start_response) __traceback_supplement__ = ServletSupplement, self, trans trans.setServlet(self) try: trans.runTransaction() trans.response().deliver() return trans.response().wsgiIterator() except self.ReturnIterException, e: return e.app_iter except self.EndResponse: trans.response().deliver() return trans.response().wsgiIterator() def runTransaction(self, trans): try: self.awake(trans) self.respond(trans) finally: self.sleep(trans) def wsgi_application(self, cls, environ, application): if self is not None: return self(environ, application) else: return cls()(environ, application) wsgi_application = classinstance.classinstancemethod( wsgi_application) # These variables are hidden in tracebacks (because they are # boring): (feel free to extend this list in your servlets!) __traceback_supplement_hide_vars__ = [ 'config', '_session', '_request', '_response', '_methodForRequestType', '_actionDict', '_title', '_transaction'] ## Access ## def name(self): """ Returns the name which is simple the name of the class. Subclasses should *not* override this method. It is used for logging and debugging. """ return self.__class__.__name__ def awake(self, trans): """ This message is sent to all objects that participate in the request-response cycle in a top-down fashion, prior to respond(). Subclasses must invoke super. """ self._transaction = trans def respond(self, trans): raise NotImplementedError def sleep(self, trans): pass ## Abilities ## def canBeThreaded(self): """ Returns 0 or 1 to indicate if the servlet can be multithreaded. This value should not change during the lifetime of the object. The default implementation returns 0. Note: This is not currently used. """ return 0 def canBeReused(self): """ Returns 0 or 1 to indicate if a single servlet instance can be reused. The default is 1, but subclasses con override to return 0. Keep in mind that performance may seriously be degraded if instances can't be reused. Also, there's no known good reasons not to reuse and instance. Remember the awake() and sleep() methods are invoked for every transaction. But just in case, your servlet can refuse to be reused. """ return 1 class HTTPServlet(Servlet): def __init__(self): Servlet.__init__(self) self._methodForRequestType = {} # a cache; see respond() ## From WebKit.HTTPServlet ## def respond(self, trans): """ Invokes the appropriate respondToSomething() method depending on the type of request (e.g., GET, POST, PUT, ...). """ httpMethodName = trans.request().method() method = self._methodForRequestType.get(httpMethodName, None) if not method: methName = 'respondTo' + httpMethodName.capitalize() method = getattr(self, methName, self.notImplemented) self._methodForRequestType[httpMethodName] = method method(trans) def notImplemented(self, trans): trans.response().setHeader('Status', '501 Not Implemented') def respondToHead(self, trans): """ A correct but inefficient implementation. Should at least provide Last-Modified and Content-Length. """ res = trans.response() w = res.write res.write = lambda *args: None self.respondToGet(trans) res.write = w class Page(HTTPServlet): class EndResponse(Exception): pass ## Server side filesystem ## def serverSidePath(self, path=None): raise NotImplementedError ## From WebKit.Page ## def awake(self, transaction): self._transaction = transaction self._response = transaction.response() self._request = transaction.request() self._session = None # don't create unless needed assert self._transaction is not None assert self._response is not None assert self._request is not None def respondToGet(self, transaction): """ Invokes _respond() to handle the transaction. """ self._respond(transaction) def respondToPost(self, transaction): """ Invokes _respond() to handle the transaction. """ self._respond(transaction) def _respond(self, transaction): """ Handles actions if an _action_ field is defined, otherwise invokes writeHTML(). Invoked by both respondToGet() and respondToPost(). """ req = transaction.request() # Check for actions for action in self.actions(): if req.hasField('_action_%s' % action) or \ req.field('_action_', None) == action or \ (req.hasField('_action_%s.x' % action) and \ req.hasField('_action_%s.y' % action)): if self._actionSet().has_key(action): self.handleAction(action) return self.writeHTML() def sleep(self, transaction): self._session = None self._request = None self._response = None self._transaction = None ## Access ## def application(self): return self.transaction().application() def transaction(self): return self._transaction def request(self): return self._request def response(self): return self._response def session(self): if not self._session: self._session = self._transaction.session() return self._session ## Generating results ## def title(self): """ Subclasses often override this method to provide a custom title. This title should be absent of HTML tags. This implementation returns the name of the class, which is sometimes appropriate and at least informative. """ return self.__class__.__name__ def htTitle(self): """ Return self.title(). Subclasses sometimes override this to provide an HTML enhanced version of the title. This is the method that should be used when including the page title in the actual page contents. """ return self.title() def htBodyArgs(self): """ Returns the arguments used for the HTML tag. Invoked by writeBody(). With the prevalence of stylesheets (CSS), you can probably skip this particular HTML feature. """ return 'color=black bgcolor=white' def writeHTML(self): """ Writes all the HTML for the page. Subclasses may override this method (which is invoked by respondToGet() and respondToPost()) or more commonly its constituent methods, writeDocType(), writeHead() and writeBody(). """ self.writeDocType() self.writeln('') self.writeHead() self.writeBody() self.writeln('') def writeDocType(self): """ Invoked by writeHTML() to write the tag. @@ sgd-2003-01-29 - restored the 4.01 transitional as per discussions on the mailing list for the 0.8 release. This implementation USED TO specify HTML 4.01 Transitional, but some versions of Mozilla acted strangely with that. The current implementation does nothing. Subclasses may override to specify something else. You can find out more about doc types by searching for DOCTYPE on the web, or visiting http://www.htmlhelp.com/tools/validator/doctype.html """ self.writeln('') pass def writeHead(self): """ Writes the portion of the page by writing the ... tags and invoking writeHeadParts() in between. """ wr = self.writeln wr('') self.writeHeadParts() wr('') def writeHeadParts(self): """ Writes the parts inside the ... tags. Invokes writeTitle() and writeStyleSheet(). Subclasses can override this to add additional items and should invoke super. """ self.writeTitle() self.writeStyleSheet() def writeTitle(self): """ Writes the portion of the page. Uses title(). """ self.writeln('\t<title>%s' % self.title()) def writeStyleSheet(self): """ Writes the style sheet for the page, however, this default implementation does nothing. Subclasses should override if necessary. A typical implementation is:: self.writeln('\t') """ pass def writeBody(self): """ Writes the portion of the page by writing the ... (making use of self.htBodyArgs()) and invoking self.writeBodyParts() in between. """ wr = self.writeln bodyArgs = self.htBodyArgs() if bodyArgs: wr('' % bodyArgs) else: wr('') self.writeBodyParts() wr('') def writeBodyParts(self): """ Invokes writeContent(). Subclasses should only override this method to provide additional page parts such as a header, sidebar and footer, that a subclass doesn't normally have to worry about writing. For writing page-specific content, subclasses should override writeContent() instead. See SidebarPage for an example override of this method. Invoked by writeBody(). """ self.writeContent() def writeContent(self): """ Writes the unique, central content for the page. Subclasses should override this method (not invoking super) to write their unique page content. Invoked by writeBodyParts(). """ self.writeln('

This page has not yet customized its content.

') ## Writing ## def write(self, *args): for arg in args: self._response.write(str(arg)) def writeln(self, *args): for arg in args: self._response.write(str(arg)) self._response.write('\n') ## Threading ## def canBeThreaded(self): """ Returns 0 because of the ivars we set up in awake(). """ return 0 ## Actions ## def handleAction(self, action): """ Invoked by `_respond` when a legitimate action has been found in a form. Invokes `preAction`, the actual action method and `postAction`. Subclasses rarely override this method. """ self.preAction(action) getattr(self, action)() self.postAction(action) def actions(self): return [] def preAction(self, actionName): raise NotImplementedError def postAction(self, actionName): raise NotImplementedError def methodNameForAction(self, name): raise NotImplementedError ## Convenience ## def htmlEncode(self, s): return wkcommon.htmlEncode(s) def htmlDecode(self, s): return wkcommon.htmlDecode(s) def urlEncode(self, s): return wkcommon.urlEncode(s) def urlDecode(self, s): return wkcommon.urlDecode(s) def forward(self, URL): self.application().forward(self.transaction(), URL) def includeURL(self, URL): raise NotImplementedError def callMethodOfServlet(self, URL, method, *args, **kwargs): raise NotImplementedError def endResponse(self): raise self.EndResponse() def sendRedirectAndEnd(self, url): """ Sends a redirect back to the client and ends the response. This is a very popular pattern. """ self.response().sendRedirect(str(url)) self.endResponse() ## Self utility ## def sessionEncode(self, url=None): """ Utility function to access session.sessionEncode. Takes a url and adds the session ID as a parameter. This is for cases where you don't know if the client will accepts cookies. """ if url == None: url = self.request().uri() return self.session().sessionEncode(url) ## Private utility ## def _actionSet(self): """ Returns a dictionary whose keys are the names returned by actions(). The dictionary is used for a quick set-membership-test in self._respond. Subclasses don't generally override this method or invoke it. """ if not hasattr(self, '_actionDict'): self._actionDict = {} for action in self.actions(): self._actionDict[action] = 1 return self._actionDict ## Validate HTML output (developer debugging) ## def validateHTML(self, closingTags=''): raise NotImplementedError PasteWebKit-1.0/paste/webkit/wksession.py0000664000175000017500000000174310516756016017156 0ustar ianbianb""" The WebKit session object; an interface surrounding a persistent dictionary. """ from wkcommon import NoDefault class Session: def __init__(self, dict): self._values = dict def invalidate(self): self._values.clear() def value(self, name, default=NoDefault): if default is NoDefault: return self._values[name] else: return self._values.get(name, default) def hasValue(self, name): return self._values.has_key(name) def setValue(self, name, value): self._values[name] = value def delValue(self, name): del self._values[name] def values(self): return self._values def setTimeout(self, timeout): # @@: This should really do something pass def __getitem__(self, name): return self.value(name) def __setitem__(self, name, value): self.setValue(name, value) def __delitem__(self, name): self.delValue(name) PasteWebKit-1.0/paste/webkit/wktransaction.py0000664000175000017500000000506310516756016020017 0ustar ianbianb""" The Webware transaction object. Responsible for creating the request and response objects, and managing some parts of the request cycle. """ from wkrequest import HTTPRequest from wkresponse import HTTPResponse from wksession import Session from wkapplication import Application class Transaction(object): def __init__(self, environ, start_response): self._environ = environ self._start_response = start_response self._request = HTTPRequest(self, environ) self._response = HTTPResponse(self, environ, start_response) self._session = None self._application = None def application(self): if self._application is None: self._application = Application(self) return self._application def request(self): return self._request def response(self): return self._response def setResponse(self, response): assert 0, "The response cannot be set" def hasSession(self): if self._session is not None: return True return self.request().environ()['paste.session.factory'].has_session() def session(self): if not self._session: self._session = Session(self.request().environ()['paste.session.factory']()) return self._session def setSession(self, session): self._session = session def servlet(self): return self._servlet def setServlet(self, servlet): self._servlet = servlet def duration(self): return self.response().endTime() - self.request().time() def errorOccurred(self): assert 0, "Not tracked" def setErrorOccurred(self, flag): assert 0, "Not tracked" def die(self): # In WebKit this looks for any instance variables with a # resetKeyBindings method, but I'm not sure why pass def writeExceptionReport(self, handler): assert 0, "Not implemented" def runTransaction(self): __traceback_hide__ = True if self._session: self._session.awake(self) self._servlet.runTransaction(self) def forward(self, url): assert self._environ.has_key('paste.recursive.forward'), \ "Forwarding is not supported (use the recursive middleware)" if url.startswith('/'): # Webware considers absolute paths to still be based off # of the Webware root; but recursive does not. url = url[1:] app_iter = self._environ['paste.recursive.forward'](url) raise self._servlet.ReturnIterException(app_iter) PasteWebKit-1.0/paste/webkit/wsgiapp.py0000664000175000017500000000712110516756016016577 0ustar ianbianbimport sys import os from paste.util import import_string, findpackage from paste import urlparser from paste.deploy.converters import asbool from paste.deploy.config import ConfigMiddleware def sys_path_install(): webware_dir = os.path.join(os.path.dirname(__file__), 'FakeWebware') if webware_dir not in sys.path: sys.path.append(webware_dir) def make_webkit_app( global_conf, servlet_directory=None, package_name=None, complete_stack=True, debug=None, # session middleware: cookie_name='_SID_', session_file_path='/tmp', session_chmod=None, # error middleware: error_email=None, error_log=None, show_exceptions_in_wsgi_errors=False, from_address=None, smtp_server=None, error_subject_prefix=None, error_message=None, # enabling: profile=False, profile_limit=40, **app_conf): sys_path_install() if package_name: package = package_name if isinstance(package, (str, unicode)): package = import_string.simple_import(package) package_dir = os.path.dirname(package.__file__) else: package = None package_dir = '' # @@: ? sys.stderr.write( 'You did not give a package or package name argument ' 'to paste.webkit.wsgiapp:make_webkit_app\n') if servlet_directory: if package_dir: servlet_directory = os.path.join(package_dir, servlet_directory) else: servlet_directory = os.path.join(package_dir, 'web') url_package_name = findpackage.find_package(servlet_directory) app = urlparser.URLParser( global_conf, servlet_directory, url_package_name) combined_conf = global_conf.copy() combined_conf.update(app_conf) app = ConfigMiddleware(app, combined_conf) if debug is None: debug = global_conf.get('debug') debug = asbool(debug) if show_exceptions_in_wsgi_errors is None: show_exceptions_in_wsgi_errors = not debug if error_subject_prefix is None: app_name = combined_conf.get('app_name') if app_name: error_subject_prefix = '[%s] ' % app_name if error_email is None: error_email = (global_conf.get('error_email') or global_conf.get('sysadmin_email')) if session_chmod is None: session_chmod = global_conf.get('session_chmod') if asbool(complete_stack): from paste import session, recursive, httpexceptions from paste.exceptions import errormiddleware app = httpexceptions.make_middleware( app, global_conf) app = recursive.RecursiveMiddleware( app, global_conf) app = session.SessionMiddleware( app, global_conf, cookie_name=cookie_name, session_file_path=session_file_path, chmod=session_chmod) app = errormiddleware.ErrorMiddleware( app, global_conf, debug=debug, error_email=error_email, show_exceptions_in_wsgi_errors=show_exceptions_in_wsgi_errors, from_address=from_address, smtp_server=smtp_server, error_subject_prefix=error_subject_prefix, error_message=error_message, ) if debug: from paste.debug import prints app = prints.PrintDebugMiddleware( app, global_conf) if asbool(profile): from paste import profilemiddleware app = profilemiddleware.ProfileMiddleware( app, global_conf, limit=int(profile_limit)) return app PasteWebKit-1.0/paste/__init__.py0000664000175000017500000000007610516756016015401 0ustar ianbianbimport pkg_resources pkg_resources.declare_namespace('paste') PasteWebKit-1.0/tests/0000775000175000017500000000000010516756100013305 5ustar ianbianbPasteWebKit-1.0/tests/sample_apps/0000775000175000017500000000000010516756100015611 5ustar ianbianbPasteWebKit-1.0/tests/sample_apps/sample1/0000775000175000017500000000000010516756100017153 5ustar ianbianbPasteWebKit-1.0/tests/sample_apps/sample1/web/0000775000175000017500000000000010516756100017730 5ustar ianbianbPasteWebKit-1.0/tests/sample_apps/sample1/web/__init__.py0000664000175000017500000000000210516756012022033 0ustar ianbianb# PasteWebKit-1.0/tests/sample_apps/sample1/web/index.py0000664000175000017500000000025010516756012021410 0ustar ianbianbfrom paste.deploy import CONFIG from WebKit.Page import Page class index(Page): def writeHTML(self): self.write('All OK; config=%s' % dict(CONFIG)) PasteWebKit-1.0/tests/sample_apps/sample1/__init__.py0000664000175000017500000000000210516756012021256 0ustar ianbianb# PasteWebKit-1.0/tests/sample_apps/__init__.py0000664000175000017500000000000210516756012017714 0ustar ianbianb# PasteWebKit-1.0/tests/sample_configs/0000775000175000017500000000000010516756100016276 5ustar ianbianbPasteWebKit-1.0/tests/sample_configs/test_deploy.ini0000664000175000017500000000030610516756012021333 0ustar ianbianb[composit:main] use = egg:PasteWebKit package_name = sample_apps.sample1 app_config1 = foo [app:second] use = main app_config2 = bar [composit:mult] use = egg:Paste#urlmap / = main /sec = second PasteWebKit-1.0/tests/conftest.py0000664000175000017500000000025210516756013015506 0ustar ianbianbimport sys, os sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) import pkg_resources pkg_resources.require('PasteDeploy') pkg_resources.require('Paste') PasteWebKit-1.0/tests/fixture.py0000664000175000017500000000053510516756013015353 0ustar ianbianbfrom paste.fixture import * import os from paste.deploy import loadapp here = os.path.dirname(__file__) def makeapp(config_file, name=None): uri = 'config:' + config_file app = loadapp(uri, name=name, relative_to=os.path.join(here, 'sample_configs')) testapp = TestApp(app) return testapp PasteWebKit-1.0/tests/test_deploy.py0000664000175000017500000000114310516756013016214 0ustar ianbianbfrom fixture import * def test_deploy(): app = makeapp('test_deploy.ini') res = app.get('/') assert 'All OK' in res assert "'app_config1': 'foo'" in res app = makeapp('test_deploy.ini', name='second') res = app.get('/') assert 'All OK' in res assert "'app_config1': 'foo'" in res assert "'app_config2': 'bar'" in res def test_deploy_mult(): app = makeapp('test_deploy.ini', name='mult') res = app.get('/') assert "'app_config1': 'foo'" in res assert "'app_config2': 'bar'" not in res res = app.get('/sec/') assert "'app_config2': 'bar'" in res PasteWebKit-1.0/setup.cfg0000664000175000017500000000135610516756100013771 0ustar ianbianb[pudge] settings = no_about=true link1=/deploy/ paste.deploy link2=/script/ paster script link3=/download/ Download extra_credits=Hosting courtesy of Tummy.com dest = docs/html docs = docs/index.txt docs/paste-webkit.txt title = Paste WebKit modules = paste.webkit theme = pythonpaste.org mailing_list_url = http://pythonpaste.org/community/mailing-list.html blog_url = http://pythonpaste.org/news/ organization = Python Paste organization_url = http://pythonpaste.org/ trac_url = http://pythonpaste.org/trac/ [stats] extra-packages = tests [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 [publish] doc-dest = scp://ianb@webwareforpython.org/home/paste/htdocs/webkit make-dirs = 1 doc-dir = docs/html PasteWebKit-1.0/setup.py0000664000175000017500000000412110516756017013662 0ustar ianbianbfrom setuptools import setup, find_packages version = '1.0' setup( name="PasteWebKit", version=version, description="A port/reimplementation of Webware WebKit in WSGI and Paste", long_description="""\ This is a reimplementation of the `Webware `_ API, using `Paste `_ for most of the functionality, and just providing an API wrapper. While the basic layout of applications is different from what Webware's ``MakeAppWorkDir`` creates, this is intended to be backward compatible for most typical Webware applications. See also the `Subversion repository `_ """, classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Internet :: WWW/HTTP :: WSGI", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", "Framework :: Paste", ], keywords='web wsgi application framework webware webkit', author="Ian Bicking", author_email="ianb@colorstudy.com", url="http://pythonpaste.org/webkit/", namespace_packages=['paste'], packages=find_packages(exclude='tests'), install_requires=['PasteDeploy', 'Paste', 'PasteScript'], zip_safe=False, package_data={ 'paste.webkit': ['paster_templates/*_tmpl', 'paster_templates/webkit/+project+.egg-info/*_tmpl', 'paster_templates/webkit/+package+/*_tmpl', 'paster_templates/webkit/+package+/web/*_tmpl', ], }, entry_points=""" [paste.app_factory] main=paste.webkit.wsgiapp:make_webkit_app [paste.paster_command] servlet=paste.webkit.servlet_script:ServletCommand [paste.paster_create_template] webkit=paste.webkit.templates:WebKit """, ) PasteWebKit-1.0/PKG-INFO0000664000175000017500000000257310516756100013247 0ustar ianbianbMetadata-Version: 1.0 Name: PasteWebKit Version: 1.0 Summary: A port/reimplementation of Webware WebKit in WSGI and Paste Home-page: http://pythonpaste.org/webkit/ Author: Ian Bicking Author-email: ianb@colorstudy.com License: UNKNOWN Description: This is a reimplementation of the `Webware `_ API, using `Paste `_ for most of the functionality, and just providing an API wrapper. While the basic layout of applications is different from what Webware's ``MakeAppWorkDir`` creates, this is intended to be backward compatible for most typical Webware applications. See also the `Subversion repository `_ Keywords: web wsgi application framework webware webkit Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python Classifier: Topic :: Internet :: WWW/HTTP Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Internet :: WWW/HTTP :: WSGI Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application Classifier: Framework :: Paste