diff options
author | Ian Bicking <ian@ianbicking.org> | 2005-08-22 23:29:31 +0000 |
---|---|---|
committer | Ian Bicking <ian@ianbicking.org> | 2005-08-22 23:29:31 +0000 |
commit | 82d331036f311709b77a1e3a90a9b191c9e49c61 (patch) | |
tree | 2c91bcd0181be8babe4146c3f448429e4840f29f | |
parent | 36d1c63f5e12f1dfd6d5187359093a96a2fd2de4 (diff) | |
download | paste-git-82d331036f311709b77a1e3a90a9b191c9e49c61.tar.gz |
Moved webkit to separate project
54 files changed, 0 insertions, 6871 deletions
diff --git a/paste/webkit/FakeWebware/MiscUtils/CSVJoiner.py b/paste/webkit/FakeWebware/MiscUtils/CSVJoiner.py deleted file mode 100644 index 2bc13a9..0000000 --- a/paste/webkit/FakeWebware/MiscUtils/CSVJoiner.py +++ /dev/null @@ -1,21 +0,0 @@ -import 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) diff --git a/paste/webkit/FakeWebware/MiscUtils/CSVParser.py b/paste/webkit/FakeWebware/MiscUtils/CSVParser.py deleted file mode 100644 index 047b3b9..0000000 --- a/paste/webkit/FakeWebware/MiscUtils/CSVParser.py +++ /dev/null @@ -1,265 +0,0 @@ -# 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<lineLen: - c = line[i] - if c=='\r': - i += 1 - if i==lineLen: - break # mac end of line - c = line[i] - if c=='\n': - i += 1 - if i==lineLen: - break # dos end of line - - self._hadParseError = 1 - raise ParseError('Newline inside string') - - elif c=='\n': - i += 1 - if i==lineLen: - break # unix end of line - - self._hadParseError = 1 - raise ParseError('Newline inside string') - - else: - if handlers[self._state](c)==Finished: # process a character - break - - i += 1 - - handlers[self._state]('\0') # signal the end of the input - - if self._state==StartRecord: - fields = self._fields - self._fields = [] - if self._stripWhitespace: - fields = [field.strip() for field in fields] - return fields - else: - return None # indicates multi-line record; eg not finished - - - ## Reset ## - - def reset(self): - """ - Resets the parser to a fresh state in order to recover from - exceptions. But if autoReset is true (the default), this is - done automatically. - """ - self._fields = [] - self._state = StartRecord - self.hasParseError = 0 - - - ## State Handlers ## - - def startRecord(self, c): - if c!='\0': # not empty line - if c=='#' and self._allowComments: - return Finished - else: - self._state = StartField - self.startField(c) - - def startField(self, c): - if c=='"': - self._state = InQuotedField # start quoted field - elif c==self._fieldSep: - self.saveField() # save empty field - elif c==' ' and self._stripWhitespace: - pass # skip over preceding whitespace - elif c=='\0': - self.saveField() # save empty field - self._state = StartRecord - else: - self.addChar(c) # begin new unquoted field - self._state = InField - - def inField(self, c): - # in unquoted field - if c==self._fieldSep: - self.saveField() - self._state = StartField - elif c=='\0': - self.saveField() # end of line - self._state = StartRecord - elif c=='"' and self._doubleQuote: - self._state = QuoteInField - else: - self.addChar(c) # normal character - - def quoteInField(self, c): - self.addChar('"') - if c=='"': - self._state = InField # save "" as " - elif c=='\0': - self.saveField() # end of line - self._state = StartRecord - elif c==self._fieldSep: - self.saveField() - self._state = StartField - else: - self.addChar(c) # normal character - self._state = InField - - def inQuotedField(self, c): - if c=='"': - if self._doubleQuote: - self._state = QuoteInQuotedField - else: - self.saveField() # end of field - self._state = EndQuotedField - elif c=='\0': - self.addChar('\n') # end of line - else: - self.addChar(c) # normal character - - def quoteInQuotedField(self, c): - if c=='"': - self.addChar('"') # save "" as " - self._state = InQuotedField - elif c==self._fieldSep: - self.saveField() - self._state = StartField - elif c==' ' and self._stripWhitespace: - pass # skip it - elif c=='\0': - self.saveField() # end of line - self._state = StartRecord - else: - self._hadParseError = 1 # illegal - raise ParseError, '%s expected after "' % self._fieldSep - - def endQuotedField(self, c): - if c==self._fieldSep: # seen closing " on quoted field - self._state = StartField # wait for new field - elif c=='\0': - self._state = StartRecord # end of line - else: - self._hadParseError = 1 - raise ParseError, '%s expected after "' % self._fieldSep - - def saveField(self): - self._fields.append(''.join(self._field)) - self._field = [] - self.addChar = self._field.append - - -# Call the global function parse() if you like the default settings of the CSVParser -_parser = CSVParser() -parse = _parser.parse - - -import types -def joinCSVFields(fields): - """ - Returns a CSV record (eg a string) from a sequence of fields. - Fields containing commands (,) or double quotes (") are quoted - 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: - newField = '"' + field + '"' - else: - newField = field - newFields.append(newField) - return ','.join(newFields) diff --git a/paste/webkit/FakeWebware/MiscUtils/DBPool.py b/paste/webkit/FakeWebware/MiscUtils/DBPool.py deleted file mode 100644 index 522f268..0000000 --- a/paste/webkit/FakeWebware/MiscUtils/DBPool.py +++ /dev/null @@ -1,128 +0,0 @@ -""" -DBPool.py - -Implements a pool of cached connections to a database. This should result in -a speedup for persistent apps. The pool of connections is threadsafe -regardless of whether the DB API module question in general has a -threadsafety of 1 or 2. - -For more information on the DB API, see: - http://www.python.org/topics/database/DatabaseAPI-2.0.html - -The idea behind DBPool is that it's completely seamless, so once you have -established your connection, use it just as you would any other DB-API -compliant module. For example: - - dbPool = DBPool(MySQLdb, 5, host=xxx, user=xxx, ...) - db = dbPool.getConnection() - -Now use "db" exactly as if it were a MySQLdb connection. It's really -just a proxy class. - -db.close() will return the connection to the pool, not actually -close it. This is so your existing code works nicely. - - -FUTURE - -* If in the presence of WebKit, register ourselves as a Can. - - -CREDIT - -* Contributed by Dan Green -* thread safety bug found by Tom Schwaller -* Fixes by Geoff Talvola (thread safety in _threadsafe_getConnection()). -* Clean up by Chuck Esterbrook. -* Fix unthreadsafe functions which were leaking, Jay Love -* Eli Green's webware-discuss comments were lifted for additional docs. -""" - - -import threading - - -class DBPoolError(Exception): pass -class UnsupportedError(DBPoolError): pass - - -class PooledConnection: - """ A wrapper for database connections to help with DBPool. You don't normally deal with this class directly, but use DBPool to get new connections. """ - - def __init__(self, pool, con): - self._con = con - self._pool = pool - - def close(self): - if self._con is not None: - self._pool.returnConnection(self) - self._con = None - - def __getattr__(self, name): - return getattr(self._con, name) - - def __del__(self): - self.close() - - -class DBPool: - - def __init__(self, dbModule, maxConnections, *args, **kwargs): - if dbModule.threadsafety==0: - raise UnsupportedError, "Database module does not support any level of threading." - elif dbModule.threadsafety==1: - from Queue import Queue - self._queue = Queue(maxConnections) - self.addConnection = self._unthreadsafe_addConnection - self.getConnection = self._unthreadsafe_getConnection - self.returnConnection = self._unthreadsafe_returnConnection - elif dbModule.threadsafety>=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) - diff --git a/paste/webkit/FakeWebware/MiscUtils/DataTable.py b/paste/webkit/FakeWebware/MiscUtils/DataTable.py deleted file mode 100644 index 1c92522..0000000 --- a/paste/webkit/FakeWebware/MiscUtils/DataTable.py +++ /dev/null @@ -1,804 +0,0 @@ -""" -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 = "<custom-type 'datetime'>" -ObjectType = "<type 'Object'>" - -_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)<len(values): - raise DataTableError, ('There are more values than headings.\nheadings(%d, %s)\nvalues(%d, %s)' % (len(self._headings), self._headings, len(values), values)) - self._values = [] - numHeadings = len(self._headings) - numValues = len(values) - assert numValues<=numHeadings - for i in range(numHeadings): - heading = self._headings[i] - if i>=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() diff --git a/paste/webkit/FakeWebware/MiscUtils/DateInterval.py b/paste/webkit/FakeWebware/MiscUtils/DateInterval.py deleted file mode 100644 index 9081ef1..0000000 --- a/paste/webkit/FakeWebware/MiscUtils/DateInterval.py +++ /dev/null @@ -1,70 +0,0 @@ -""" -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] diff --git a/paste/webkit/FakeWebware/MiscUtils/DictForArgs.py b/paste/webkit/FakeWebware/MiscUtils/DictForArgs.py deleted file mode 100644 index 85cc545..0000000 --- a/paste/webkit/FakeWebware/MiscUtils/DictForArgs.py +++ /dev/null @@ -1,209 +0,0 @@ -""" -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): - """ - 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<sLen: - for regEx in REs: - if verbose: print '>> 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<matchesLen: - match = matches[i] - if i+1<matchesLen: - peekMatch = matches[i+1] - else: - peekMatch = None - if match.re is nameRE: - if peekMatch is not None: - if peekMatch.re is nameRE: - # We have a name without an explicit value - dict[match.group()] = '1' - i = i + 1 - continue - if peekMatch.re is equalsRE: - if i+2<matchesLen: - target = matches[i+2] - if target.re is nameRE or target.re is stringRE: - value = target.group() - if value[0]=="'" or value[0]=='"': - value = value[1:-1] - #value = "'''%s'''" % value[1:-1] - #value = eval(value) - dict[match.group()] = value - i = i + 3 - continue - else: - dict[match.group()] = '1' - i = i + 1 - continue - _SyntaxError(s) - - - if verbose: print - - return dict - - -from string import letters - -def PyDictForArgs(s): - """ - Takes an input such as: - x=3 - name="foo" - first='john'; last='doe' - list=[1, 2, 3]; name='foo' - - And returns a dictionary representing the same. - - All values are interpreted as Python expressions. Any error in these - expressions will raise the appropriate Python exception. This syntax - allows much more power than DictForArgs() since you can include - lists, dictionaries, actual ints and floats, etc. - - This could also open the door to hacking your software if the input - comes from a tainted source such as an HTML form or an unprotected - configuration file. - - Returns {} for an empty string. - - See also: DictForArgs() and ExpandDictWithExtras() in this module - """ - if s: - s = s.strip() - if not s: - return {} - - # special case: just a name - # meaning: name=1 - # example: isAbstract - if s.find(' ')==-1 and s.find('=')==-1 and s[0] in letters: - s += '=1' - - results = {} - exec s in results - - del results['__builtins__'] - return results - - -def ExpandDictWithExtras(dict, key='Extras', delKey=1, dictForArgs=DictForArgs): - """ - Returns a dictionary with the 'Extras' column expanded by DictForArgs(). For example, given: - { 'Name': 'foo', 'Extras': 'x=1 y=2' } - The return value is: - { 'Name': 'foo', 'x': '1', 'y': '2' } - The key argument controls what key in the dictionary is used to hold the extra arguments. The delKey argument controls whether that key and its corresponding value are retained. - The same dictionary may be returned if there is no extras key. - The most typical use of this function is to pass a row from a DataTable that was initialized from a CSV file (e.g., a spreadsheet or tabular file). FormKit and MiddleKit both use CSV files and allow for an Extras column to specify attributes that occur infrequently. - """ - - if dict.has_key(key): - newDict = {} - # We use the following for loop rather than newDict.update() - # so that the dict arg can be dictionary-like. - for k, v in dict.items(): - newDict[k] = v - if delKey: - del newDict[key] - newDict.update(dictForArgs(dict[key])) - return newDict - else: - return dict diff --git a/paste/webkit/FakeWebware/MiscUtils/Error.py b/paste/webkit/FakeWebware/MiscUtils/Error.py deleted file mode 100644 index b26f3e1..0000000 --- a/paste/webkit/FakeWebware/MiscUtils/Error.py +++ /dev/null @@ -1,50 +0,0 @@ -from UserDict import UserDict - - -class Error(UserDict): - """ - An error is a dictionary-like object, containing a specific user-readable error message and an object associated with it. Since Error inherits UserDict, other informative values can be arbitrarily attached to errors. For this reason, subclassing Error is rare. - - Example: - err = Error(user, 'Invalid password.') - err['time'] = time.time() - err['attempts'] = attempts - - The object and message can be accessed via methods: - print err.object() - print err.message() - - When creating errors, you can pass None for both the object and the message. You can also pass additional values, which are then included in the error: - >>> 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 diff --git a/paste/webkit/FakeWebware/MiscUtils/Funcs.py b/paste/webkit/FakeWebware/MiscUtils/Funcs.py deleted file mode 100644 index 236f925..0000000 --- a/paste/webkit/FakeWebware/MiscUtils/Funcs.py +++ /dev/null @@ -1,325 +0,0 @@ -""" -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 <pre> </pre> tags since <pre> 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 hanging<width - hanging = ' ' * hanging - lines = string.split(s, '\n') - i = 0 - while i<len(lines): - s = lines[i] - while len(s)>width: - 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 whose keys give 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) diff --git a/paste/webkit/FakeWebware/MiscUtils/M2PickleRPC.py b/paste/webkit/FakeWebware/MiscUtils/M2PickleRPC.py deleted file mode 100644 index 2976f7f..0000000 --- a/paste/webkit/FakeWebware/MiscUtils/M2PickleRPC.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -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) - diff --git a/paste/webkit/FakeWebware/MiscUtils/MixIn.py b/paste/webkit/FakeWebware/MiscUtils/MixIn.py deleted file mode 100644 index 26dde1d..0000000 --- a/paste/webkit/FakeWebware/MiscUtils/MixIn.py +++ /dev/null @@ -1,70 +0,0 @@ -from 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) diff --git a/paste/webkit/FakeWebware/MiscUtils/NamedValueAccess.py b/paste/webkit/FakeWebware/MiscUtils/NamedValueAccess.py deleted file mode 100644 index 62df004..0000000 --- a/paste/webkit/FakeWebware/MiscUtils/NamedValueAccess.py +++ /dev/null @@ -1,570 +0,0 @@ -""" -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 <echuck@mindspring.com> -Tavis Rudd <tavis@calrudd.com> -""" - - -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() diff --git a/paste/webkit/FakeWebware/MiscUtils/ParamFactory.py b/paste/webkit/FakeWebware/MiscUtils/ParamFactory.py deleted file mode 100644 index 4b3f2f3..0000000 --- a/paste/webkit/FakeWebware/MiscUtils/ParamFactory.py +++ /dev/null @@ -1,22 +0,0 @@ -from 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() - diff --git a/paste/webkit/FakeWebware/MiscUtils/PickleCache.py b/paste/webkit/FakeWebware/MiscUtils/PickleCache.py deleted file mode 100644 index e826b89..0000000 --- a/paste/webkit/FakeWebware/MiscUtils/PickleCache.py +++ /dev/null @@ -1,220 +0,0 @@ -""" -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)<os.path.getmtime(filename): - #if v: print 'cache is out of date' - shouldDeletePickle = 1 - else: - try: - #if v: print 'about to open for read %r' % picklePath - file = open(picklePath) - except IOError, e: - #if v: print 'cannot open cache file: %s: %s' % (e.__class__.__name__, e) - pass - else: - try: - #if v: print 'about to load' - dict = load(file) - except EOFError: - #if v: print 'EOFError - not loading' - shouldDeletePickle = 1 - else: - file.close() - #if v: print 'finished reading' - assert isinstance(dict, DictType), 'type=%r dict=%r' % (type(dict), dict) - for key in ('source', 'data', 'pickle version', 'python version'): - assert dict.has_key(key), key - if source and dict['source']!=source: - #if v: print 'not from required source (%s): %s' % (source, dict['source']) - shouldDeletePickle = 1 - elif dict['pickle version']!=pickleVersion: - #if v: print 'pickle version (%i) does not match expected (%i)' % (dict['pickle version'], pickleVersion) - shouldDeletePickle = 1 - elif dict['python version']!=sys.version_info: - #if v: print 'python version %s does not match current %s' % (dict['python version'], sys.version_info) - shouldDeletePickle = 1 - else: - #if v: print 'all tests pass. accepting data' - if v>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 diff --git a/paste/webkit/FakeWebware/MiscUtils/PickleRPC.py b/paste/webkit/FakeWebware/MiscUtils/PickleRPC.py deleted file mode 100644 index c74f651..0000000 --- a/paste/webkit/FakeWebware/MiscUtils/PickleRPC.py +++ /dev/null @@ -1,428 +0,0 @@ -""" -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) - diff --git a/paste/webkit/FakeWebware/MiscUtils/PropertiesObject.py b/paste/webkit/FakeWebware/MiscUtils/PropertiesObject.py deleted file mode 100644 index d32f1a3..0000000 --- a/paste/webkit/FakeWebware/MiscUtils/PropertiesObject.py +++ /dev/null @@ -1,155 +0,0 @@ -from 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)<tuple(self['requiredPyVersion']): - raise WillNotRunError, 'Required python ver is %s, but actual ver is %s.' % (self['requiredPyVersion'], pyVer) - - def checkRequiredOpSys(self): - requiredOpSys = self.get('requiredOpSys', None) - if requiredOpSys: - # We accept a string or list of strings - if type(requiredOpSys)==types.StringType: - requiredOpSys = [requiredOpSys] - if not os.name in requiredOpSys: - raise WillNotRunError, 'Required op sys is %s, but actual op sys is %s.' % (requiredOpSys, os.name) - - def checkDeniedOpSys(self): - deniedOpSys = self.get('deniedOpSys', None) - if deniedOpSys: - # We accept a string or list of strings - if type(deniedOpSys)==types.StringType: - deniedOpSys = [deniedOpSys] - if os.name in deniedOpSys: - raise WillNotRunError, 'Will not run on op sys %s and actual op sys is %s.' % (deniedOpSys, os.name) - - def checkRequiredSoftware(self): - """ Not implemented. No op right now. """ - # Check required software - # @@ 2001-01-24 ce: TBD - # Issues include: - # - order of dependencies - # - circular dependencies - # - examining Properties and willRun of dependencies - reqSoft = self.get('requiredSoftware', None) - if reqSoft: - for soft in reqSoft: - # type, name, version - pass - - def checkWillRunFunc(self): - willRunFunc = self.get('willRunFunc', None) - if willRunFunc: - whyNotMsg = willRunFunc() - if whyNotMsg: - raise WillNotRunError, whyNotMsg diff --git a/paste/webkit/FakeWebware/MiscUtils/__init__.py b/paste/webkit/FakeWebware/MiscUtils/__init__.py deleted file mode 100644 index 0f39efb..0000000 --- a/paste/webkit/FakeWebware/MiscUtils/__init__.py +++ /dev/null @@ -1,68 +0,0 @@ -# MiscUtils component -# Webware for Python -# See Docs/index.html - -__all__ = ['Configurable', 'DBPool', 'DataTable', 'DictForArgs', 'Error', 'Funcs', 'MixIn', 'NamedValueAccess', 'PropertiesObject', 'unittest'] - - -try: - AbstractError # Python might build this in some day. -except NameError: - class AbstractError(NotImplementedError): - """ - This exception is raised by abstract methods in abstract classes. It - is a special case of NotImplementedError, that indicates that the - implementation won't ever be provided at that location in the future - --instead the subclass should provide it. - - Typical usage: - - from MiscUtils import AbstractError - - class Foo: - def bar(self): - raise AbstractError, self.__class__ - - Note that added the self.__class__ makes the resulting exception - *much* more useful. - """ - pass - -# @@ 2002-11-10 ce: SubclassResponsibilityError is deprecated in favor of AbstractError, post 0.7 -SubclassResponsibilityError = AbstractError - - -class NoDefault: - """ - This provides a singleton "thing" which can be used to initialize - the "default=" arguments for different retrieval methods. For - example: - - from MiscUtils import NoDefault - def bar(self, name, default=NoDefault): - if default is NoDefault: - return self._bars[name] # will raise exception for invalid key - else: - return self._bars.get(name, default) - - The value None does not suffice for "default=" because it does not - indicate whether or not a value was passed. - - Consistently using this singleton is valuable due to subclassing - situations: - - def bar(self, name, default=NoDefault): - if someCondition: - return self.specialBar(name) - else: - return SuperClass.bar(name, default) - - It's also useful if one method that uses "default=NoDefault" relies - on another object and method to which it must pass the default. - (This is similar to the subclassing situation.) - """ - pass - - -def InstallInWebKit(appServer): - pass diff --git a/paste/webkit/FakeWebware/README.txt b/paste/webkit/FakeWebware/README.txt deleted file mode 100644 index d6a7943..0000000 --- a/paste/webkit/FakeWebware/README.txt +++ /dev/null @@ -1,7 +0,0 @@ -This 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. diff --git a/paste/webkit/FakeWebware/TaskKit/Docs/GenIndex.css b/paste/webkit/FakeWebware/TaskKit/Docs/GenIndex.css deleted file mode 100644 index a95df2f..0000000 --- a/paste/webkit/FakeWebware/TaskKit/Docs/GenIndex.css +++ /dev/null @@ -1,46 +0,0 @@ -<style type="text/css"> -<!-- - /** - This style sheet is linked to by both Webware's index.html - as well as each component's index.html. - */ - - p { - padding-left: 0; - } - dl { - padding-left: 0; - } - ol { - padding-left: 0; - } - ul { - list-style-type: disc; - padding-left: 0; - } - li { - padding-top: 0; - } - a.BigLink { - font-size: larger; - font-weight: bold; - } - td.InfoLabel { - font-family: Arial, Helvetica, sans-serif; - background-color: #DDD; - } - td.InfoValue { - font-family: Arial, Helvetica, sans-serif; - background-color: #EEE; - } - td.InfoSummary { - font-family: Arial, Helvetica, sans-serif; - font-size: smaller; - background-color: #EEE; - } - td.DocList { - font-family: Arial, Helvetica, sans-serif; - } - ---> -</style> diff --git a/paste/webkit/FakeWebware/TaskKit/Docs/QuickStart.html b/paste/webkit/FakeWebware/TaskKit/Docs/QuickStart.html deleted file mode 100644 index 97e81b8..0000000 --- a/paste/webkit/FakeWebware/TaskKit/Docs/QuickStart.html +++ /dev/null @@ -1,408 +0,0 @@ -<HTML> -<HEAD> -<TITLE>TaskKit QuickStart</TITLE> -</HEAD> - -<BODY BGCOLOR="WHITE"> - -<H4>Tom's Webware Documentation</H4> -<H1>Scheduling with Python and Webware</H1> -<a href="mailto:Tom.Schwaller@linux-community.de" style="text-decoration: none; color : #009999;">Tom Schwaller</a> - -<P> -<HR NOSHADE> -<I><FONT COLOR="#0000ff"> -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. -</FONT></I> -<HR NOSHADE> - -<P> -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 <code>cron</code> 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). - -<P> -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 -<code>sched.py</code> and <code>bisect.py</code> 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. - -<P> -After some test implementations I discovered the Java scheduling framework of the -<a href="http://www.arlut.utexas.edu/gash2/">Ganymede</a> 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. - -<h2>Tasks</h2> - -<P> -The TaskKit implements the three classes <code>Scheduler, TaskHandler</code> and <code>Task</code>. -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 <code>run()</code>-method like in -the following example: - -<p> -<table border=0 bgcolor="#eeeeee" width="100%"><tr><td> -<pre> -<FONT COLOR=black><B>from</B></FONT> TaskKit.Task <FONT COLOR=black><B>import</B></FONT> Task -<FONT COLOR=black><B>from</B></FONT> time <FONT COLOR=black><B>import</B></FONT> time, strftime, localtime - -<FONT COLOR=black><B>class</B></FONT><A NAME="SimpleTask"><FONT COLOR=#AA00AA><B> SimpleTask</B></FONT></A>(Task): - <FONT COLOR=black><B>def</B></FONT><A NAME="run"><FONT COLOR=#AA00AA><B> run</B></FONT></A>(self): - <FONT COLOR=black><B>print</B></FONT> self.name(), strftime(<FONT COLOR=#FF0000>"%H:%M:%S"</FONT>, localtime(time())) -</pre> -</td></tr></table> - -<p> -<code>self.name()</code> 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 <code>WebKit/Tasks/SessionTask.py</code>. - -<p> -<table border=0 bgcolor="#eeeeee" width="100%"><tr><td> -<pre> -<FONT COLOR=black><B>from</B></FONT> TaskKit.Task <FONT COLOR=black><B>import</B></FONT> Task - -<FONT COLOR=black><B>class</B></FONT><A NAME="SessionTask"><FONT COLOR=#AA00AA><B> SessionTask</B></FONT></A>(Task): - <FONT COLOR=black><B>def</B></FONT><A NAME="__init__"><FONT COLOR=#AA00AA><B> __init__</B></FONT></A>(self, sessions): - Task.__init__(self) - self._sessionstore = sessions - - <FONT COLOR=black><B>def</B></FONT><A NAME="run"><FONT COLOR=#AA00AA><B> run</B></FONT></A>(self): - <FONT COLOR=black><B>if</B></FONT> self.proceed(): - self._sessionstore.cleanStaleSessions(self) -</pre> -</td></tr></table> - -<p> -Here you see the <code>proceed()</code> 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 <code>stopTask()</code> call which is not recommented though. -It's generally better to let the task finish and use the <code>unregister()</code> and -<code>disable()</code> 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 <code>proceed()</code> - -<p> -<table border=0 bgcolor="#eeeeee" width="100%"><tr><td> -<pre> -<FONT COLOR=black><B>def</B></FONT><A NAME="proceed"><FONT COLOR=#AA00AA><B> proceed</B></FONT></A>(self): - <FONT COLOR=#FF0000>""" - 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. - """</FONT> - <FONT COLOR=black><B>return</B></FONT> <FONT COLOR=black><B>not</B></FONT>( self._close.isSet() <FONT COLOR=black><B>or</B></FONT> (<FONT COLOR=black><B>not</B></FONT> self._handle._isRunning) ) -</pre> -</td></tr></table> - -<P> -uses the <code>_close</code> Event variable, which was also available trough the <code>close()</code> method. -Don't count on that in future versions, it will probably be removed. Just use <code>proceed()</code> instead -(take a look at <code>TaskKit/Tests/BasicTest.py</code>). Another API change after version 0.5 of Webware was -the removal of the <code>close</code> variable in <code>run()</code>. 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 -<code>close</code> 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 <code>TaskManager</code>, which will be discussed shortly. Let's look at some more -examples first. - -<h2>Generating static pages</h2> - -<P> -On a high trafic web site (a la <a href="http://slashdot.org">slashdot</a>) 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 <code>cron</code> jobs. -Let's look a litle bit closer at the static generation technique now. First of all we -need a <code>PageGenerator</code> 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. - -<p> -<table border=0 bgcolor="#eeeeee" width="100%"><tr><td> -<pre> -<FONT COLOR=black><B>from</B></FONT> TaskKit.Task <FONT COLOR=black><B>import</B></FONT> Task -<FONT COLOR=black><B>from</B></FONT> time <FONT COLOR=black><B>import</B></FONT> * - -html = <FONT COLOR=#FF0000>'''<html> -<head><title>%s</title></head> -<body bgcolor="white"> -<h1>%s</h1> -</body> -</html> -'''</FONT> - -<FONT COLOR=black><B>class</B></FONT><A NAME="PageGenerator"><FONT COLOR=#AA00AA><B> PageGenerator</B></FONT></A>(Task): - <FONT COLOR=black><B>def</B></FONT><A NAME="__init__"><FONT COLOR=#AA00AA><B> __init__</B></FONT></A>(self, filename): - Task.__init__(self) - self._filename = filename - - <FONT COLOR=black><B>def</B></FONT><A NAME="run"><FONT COLOR=#AA00AA><B> run</B></FONT></A>(self): - f = open(self._filename, <FONT COLOR=#FF0000>'w'</FONT>) - now = asctime(localtime(time())) - f.write( html % (<FONT COLOR=#FF0000>'Static Page'</FONT>, now) ) - f.close() -</pre> -</td></tr></table> - -<h2>Scheduling</h2> - -<P> -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 <code>__init__.py</code> file to make this folder a Python -package). First of all we create a new <code>Scheduler</code> object, start it as a thread and add -a periodic page generation object (of type <code>PageGenerator</code>) with the -<code>addPeriodicAction</code> method (this will probably be changed in the near future to -the more constitent name <code>addPeriodicTask</code>). 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). - -<p> -<table border=0 bgcolor="#eeeeee" width="100%"><tr><td> -<PRE> -<FONT COLOR=black><B>from</B></FONT> TaskKit.Scheduler <FONT COLOR=black><B>import</B></FONT> Scheduler -<FONT COLOR=black><B>from</B></FONT> Tasks.PageGenerator <FONT COLOR=black><B>import</B></FONT> PageGenerator -<FONT COLOR=black><B>from</B></FONT> time <FONT COLOR=black><B>import</B></FONT> * - -<FONT COLOR=black><B>def</B></FONT><A NAME="main"><FONT COLOR=#AA00AA><B> main</B></FONT></A>(): - scheduler = Scheduler() - scheduler.start() - scheduler.addPeriodicAction(time(), 5, PageGenerator(<FONT COLOR=#FF0000>'static.html'</FONT>), <FONT COLOR=#FF0000>'PageGenerator'</FONT>) - sleep(20) - scheduler.stop() - -<FONT COLOR=black><B>if</B></FONT> __name__==<FONT COLOR=#FF0000>'__main__'</FONT>: - main() -</PRE> -</td></tr></table> - -<P> -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 <code>wait()</code> function in the Python <code>threading</code> 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 <code>scheduler.nextTime()</code>. This is -achieved through the <code>notify()</code> method, which sets the <code>notifyEvent</code> -(<code>scheduler._notifyEvent.set()</code>). On Unix we could use <code>sleep</code> and a <code>signal</code> -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 <code>cron</code> 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). - -<h2>Generating static pages again</h2> - -<P> -Let's refine our example a little bit and plug it into Webware. We will write a Python servlet which loks like this: - -<P> -<center> -<form method="post"> -<input type=submit name=_action_ value=Generate> -<input type=text name=filename value="static.html" size=20> every -<input type=text name=seconds value=60 size=5> seconds</form> - -<table width=50% border=1 cellspacing=0> -<tr bgcolor=00008B><th colspan=2><font color=white>Task List</font></th></tr> -<tr bgcolor=#dddddd><td><b>Task Name</b></td><td><b>Period</b></td></tr> -<tr><td>SessionSweeper</td><td>360</td></tr> -<tr><td>PageGenerator for static3.html</td><td>30</td></tr> -<tr><td>PageGenerator for static1.html</td><td>60</td></tr> -<tr><td>PageGenerator for static2.html</td><td>120</td></tr> -</table> -</center> - -<P> -When you click on the <code>Generate</code> button a new periodic <code>PageGenerator</code> task -will be added to the Webware scheduler. Remember that this will generate a static page <code>static.html</code> -every 60 seconds (if you use the default values). The new task name is <code>"PageGenerator for filename"</code>, -so you can use this servlet to change the settings of already scheduled tasks (by rescheduling) or -add new <code>PageGenerator</code> tasks with different filenames. This is quite useless here, but as -soon as you begin to parametrize your <code>Task</code> 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!). - -<P> -Finally we come to the servlet code, which should be more or less self explanatory, except for the -<code>_action_</code> construct which is very well explained in the Webware documentation though (just -in case you forgot that). <code>app.taskManager()</code> 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. <code>PersistantScheduler</code> 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. - -<p> -<table border=0 bgcolor="#eeeeee" width="100%"><tr><td> -<PRE> -<FONT COLOR=black><B>import</B></FONT> os, string, time -<FONT COLOR=black><B>from</B></FONT> ExamplePage <FONT COLOR=black><B>import</B></FONT> ExamplePage -<FONT COLOR=black><B>from</B></FONT> Tasks.PageGenerator <FONT COLOR=black><B>import</B></FONT> PageGenerator - -<FONT COLOR=black><B>class</B></FONT><A NAME="Schedule"><FONT COLOR=#AA00AA><B> Schedule</B></FONT></A>(ExamplePage): - - <FONT COLOR=black><B>def</B></FONT><A NAME="writeContent"><FONT COLOR=#AA00AA><B> writeContent</B></FONT></A>(self): - wr = self.write - wr(<FONT COLOR=#FF0000>'<center><form method="post">'</FONT>) - wr(<FONT COLOR=#FF0000>'<input type=submit name=_action_ value=Generate> '</FONT>) - wr(<FONT COLOR=#FF0000>'<input type=text name=filename value="static.html" size=20> every '</FONT>) - wr(<FONT COLOR=#FF0000>'<input type=text name=seconds value=60 size=5> seconds'</FONT>) - wr(<FONT COLOR=#FF0000>'</form>'</FONT>) - wr(<FONT COLOR=#FF0000>'<table width=50% border=1 cellspacing=0>'</FONT>) - wr(<FONT COLOR=#FF0000>'<tr bgcolor=00008B><th colspan=2><font color=white>Task List</font></th></tr>'</FONT>) - wr(<FONT COLOR=#FF0000>'<tr bgcolor=#dddddd><td><b>Task Name</b></td><td><b>Period</b></td></tr>'</FONT>) - <FONT COLOR=black><B>for</B></FONT> taskname, handler <FONT COLOR=black><B>in</B></FONT> self.application().taskManager().scheduledTasks().items(): - wr(<FONT COLOR=#FF0000>'<tr><td>%s</td><td>%s</td></tr>'</FONT> % (taskname, handler.period())) - wr(<FONT COLOR=#FF0000>'</table></center>'</FONT>) - - <FONT COLOR=black><B>def</B></FONT><A NAME="generate"><FONT COLOR=#AA00AA><B> generate</B></FONT></A>(self, trans): - app = self.application() - tm = app.taskManager() - req = self.request() - <FONT COLOR=black><B>if</B></FONT> req.hasField(<FONT COLOR=#FF0000>'filename'</FONT>) <FONT COLOR=black><B>and</B></FONT> req.hasField(<FONT COLOR=#FF0000>'seconds'</FONT>): - self._filename = req.field(<FONT COLOR=#FF0000>'filename'</FONT>) - self._seconds = string.atoi(req.field(<FONT COLOR=#FF0000>'seconds'</FONT>)) - task = PageGenerator(app.serverSidePath(<FONT COLOR=#FF0000>'Examples/'</FONT> + self._filename)) - taskname = <FONT COLOR=#FF0000>'PageGenerator for '</FONT> + self._filename - tm.addPeriodicAction(time.time(), self._seconds, task, taskname) - self.writeBody() - - <FONT COLOR=black><B>def</B></FONT><A NAME="methodNameForAction"><FONT COLOR=#AA00AA><B> methodNameForAction</B></FONT></A>(self, name): - <FONT COLOR=black><B>return</B></FONT> string.lower(name) - - <FONT COLOR=black><B>def</B></FONT><A NAME="actions"><FONT COLOR=#AA00AA><B> actions</B></FONT></A>(self): - <FONT COLOR=black><B>return</B></FONT> ExamplePage.actions(self) + [<FONT COLOR=#FF0000>'generate'</FONT>] -</PRE> -</td></tr></table> - -<p> -<h2>The Scheduler</h2> - -<P> -Now it's time to take a closer look at the <code>Scheduler</code> class itself. As you have seen in the examples above, -writing tasks is only a matter of overloading the <code>run()</code> method in a derived class and adding it to the -scheduler with <code>addTimedAction, addActionOnDemand, addDailyAction</code> or <code>addPeriodicAction</code>. -The scheduler will wrap the Task in a <code>TaskHandler</code> structure which knows all the scheduling details -and add it to its <code>_scheduled</code> or <code>_onDemand</code> dictionaries. The latter is populated by -<code>addActionOnDemand</code> and contains tasks which can be called any time by -<code>scheduler.runTaskNow('taskname')</code> as you can see in the following example. After that -the task has gone. - -<p> -<table border=0 bgcolor="#eeeeee" width="100%"><tr><td> -<PRE> -scheduler = Scheduler() -scheduler.start() -scheduler.addActionOnDemand(SimpleTask(), <FONT COLOR=#FF0000>'SimpleTask'</FONT>) -sleep(5) -<FONT COLOR=black><B>print</B></FONT> <FONT COLOR=#FF0000>"Demanding SimpleTask"</FONT> -scheduler.runTaskNow(<FONT COLOR=#FF0000>'SimpleTask'</FONT>) -sleep(5) -scheduler.stop() -</PRE> -</td></tr></table> - -<p> -If you need a task more than one time it's better to start it regularly with one of -the <code>add*Action</code> methods first. It will be added to the <code>_scheduled</code> -dictionary. If you do not need the task for a certain time disable it with -<code>scheduler.disableTask('taskname')</code> and enable it later with -<code>scheduler.enableTask('taskname')</code>. There are some more methods -(e.g. <code>demandTask(), stopTask(), ...</code>) in the <code>Scheduler</code> 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 ;-)). - -<P> -When a periodic task is scheduled it is added in a wrapped version to the -<code>_scheduled</code> 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 -<code>_runnning</code> dictionary. After completion of the task thread the handle -reschedules the task by putting it back from <code>_running</code> to <code>_scheduled</code>), -calculating the next execution time <code>nextTime</code> 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 <code>runOnCompletion</code> 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 <code>running('taskname), -scheduled('taskname')</code> and <code>onDemand('taskname')</code> methods. - -<P> -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. - -<p> -<table border=0 bgcolor="#eeeeee" width="100%"><tr><td> -<PRE> -<FONT COLOR=black><B>class</B></FONT><A NAME="SimpleTask"><FONT COLOR=#AA00AA><B> SimpleTask</B></FONT></A>(Task): - - <FONT COLOR=black><B>def</B></FONT><A NAME="run"><FONT COLOR=#AA00AA><B> run</B></FONT></A>(self): - <FONT COLOR=black><B>if</B></FONT> self.proceed(): - <FONT COLOR=black><B>print</B></FONT> self.name(), time() - <B>print</b> "Increasing period" - self.handle().setPeriod(self.handle().period()+2) - <FONT COLOR=black><B>else</B></FONT>: - <FONT COLOR=black><B>print</B></FONT> <FONT COLOR=#FF0000>"Should not proceed"</FONT>, self.name() -</PRE> -</td></tr></table> - -<p> -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) - -<P> -<TABLE BORDER=1 CELLSPACING=0 CELLPADDING=0 WIDTH="100%"> -<TR ALIGN=CENTER BGCOLOR="#DFEFFF"> -<TD ALIGN=CENTER><FONT SIZE=+2>Info</FONT> -</TD> -</TR> -<TR ALIGN=LEFT VALIGN=TOP> -<TD> -<BR> -[1] Webware: <a href="http://webware.sourceforge.net/">http://webware.sourceforge.net/</a> -<br> -[2] Ganymede: <a href="http://www.arlut.utexas.edu/gash2/">http://www.arlut.utexas.edu/gash2/</a> -<br> -</TD> -</TR> -</TABLE> - -<P> -Published under the <a href="http://www.gnu.org/copyleft/fdl.html">GNU Free Documentation License</a>. - -</BODY> -</HTML> diff --git a/paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-0.1.1.html b/paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-0.1.1.html deleted file mode 100644 index f6c71a8..0000000 --- a/paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-0.1.1.html +++ /dev/null @@ -1,28 +0,0 @@ -<html> - -<head> - <link rel=stylesheet href=StyleSheet.css type=text/css> - <title>TaskKit 0.1.1 Release Notes</title> -</head> - -<body> - -<h1>TaskKit 0.1.1 Release Notes</h1> - - -<a name=ReleaseNotes><h2>Release Notes</h2></a> - -<ul> - <li> Version 0.1.1 was released on 3/@@/2001. -</ul> - -<p> This release is mainly a cleanup of some aspects of the code. The following changes affect the external interface. -<ul> -<li> Removed the <code>close</code> 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. - -<li> Various other cleanups. None affecting the core functionality. -</ul> - - -</body> -</html> diff --git a/paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-0.7.html b/paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-0.7.html deleted file mode 100644 index 0a91680..0000000 --- a/paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-0.7.html +++ /dev/null @@ -1,24 +0,0 @@ -<html> - -<head> - <link rel=stylesheet href=StyleSheet.css type=text/css> - <title>TaskKit 0.7 Release Notes</title> -</head> - -<body> - -<h1>TaskKit 0.7 Release Notes</h1> - - -<a name=ReleaseNotes><h2>Release Notes</h2></a> - -<a name=Changes><h2>Changes</h2></a> -<ul> - <li> New versioning strategy: all Webware components will now have their version numbers updated in lock-step. -</ul> - - -<p><hr> - -</body> -</html> diff --git a/paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-0.8.html b/paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-0.8.html deleted file mode 100644 index 5ed9271..0000000 --- a/paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-0.8.html +++ /dev/null @@ -1,24 +0,0 @@ -<html> - -<head> - <link rel=stylesheet href=StyleSheet.css type=text/css> - <title>TaskKit 0.8 Release Notes</title> -</head> - -<body> - -<h1>TaskKit 0.8 Release Notes</h1> - - -<a name=ReleaseNotes><h2>Release Notes</h2></a> - -<a name=Changes><h2>Changes</h2></a> -<ul> - <li> New versioning strategy: all Webware components will now have their version numbers updated in lock-step. -</ul> - - -<p><hr> - -</body> -</html> diff --git a/paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-X.Y.html b/paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-X.Y.html deleted file mode 100644 index 9b13c30..0000000 --- a/paste/webkit/FakeWebware/TaskKit/Docs/RelNotes-X.Y.html +++ /dev/null @@ -1,75 +0,0 @@ -<html> - -<head> - <link rel=stylesheet href=StyleSheet.css type=text/css> - <style type=text/css> - <!-- - li { padding-bottom: 0.75em; - } - --> - </style> - - <!-- - Auto versioning is handled by Webware/bin/setversion.py. - This will automatically update version and release date - information embedded within HTML comment tags. - - Note however that comment tags within a title block seem to - not be interpreted as comments in Mozilla 1.2.1 - --> - <title>CGIWrapper Release Notes</title> -</head> - -<body> - -<h1>CGIWrapper Release Notes</h1> -Version <!-- version --> X.Y <!-- /version --> <br> -Webware for Python <!-- version --> X.Y <!-- /version --> -Released on <!-- relDate --> @@/@@/@@ <!-- /relDate --> - - -<a name=Introduction<h2>Introduction</h2></a> -<ul> - <li> * -</ul> - -<h2>Major Changes</h2> -<ul> - <li> * -</ul> - - -<h2>New Features</h2> -<ul> - <li> * -</ul> - - -<h2>Improvements and Refinements</h2> -<ul> - <li> * -</ul> - - -<h2>Security</h2> -<ul> - <li> * -</ul> - - -<h2>Minor API Changes</h2> -<ul> - <li> * -</ul> - - -<h2>Bugfixes</h2> -<ul> - <li> * -</ul> - - -<p><hr> - -</body> -</html> diff --git a/paste/webkit/FakeWebware/TaskKit/Docs/StyleSheet.css b/paste/webkit/FakeWebware/TaskKit/Docs/StyleSheet.css deleted file mode 100644 index db08e4f..0000000 --- a/paste/webkit/FakeWebware/TaskKit/Docs/StyleSheet.css +++ /dev/null @@ -1,88 +0,0 @@ -<style type="text/css"> -<!-- - h1 { font-family: Tahoma, Arial, Helvetica, sans-serif; - font-size: 18pt; - padding-top: 0; - padding-bottom: 0; - } - h2 { font-family: Arial, Helvetica, sans-serif; - font-size: 12pt; - color: darkblue; - padding-top: 1em; - padding-bottom: 0; - } - h3 { font-family: Arial, Helvetica, sans-serif; - font-size: 11pt; - padding-top: 1em; - padding-bottom: 0; - } - h4 { font-family: Arial, Helvetica, sans-serif; - font-size: 10pt; - padding-top: 1em; - padding-bottom: 0; - } - p { padding-left: 2em; - color: black; - } - dl { padding-left: 2em; - } - ol { padding-left: 0.5em; - } - ul { list-style-type: disc; - padding-left: 0.5em; - } - li { padding-top: 0em; - } - code{ color: darkblue; - } - body{ background-color: #FFFFFF; - } - - .TitleBar { - font-family: Tahoma, Arial, Helvetica, sans-serif; - font-size: 18pt; - padding-top: 0.5ex; - padding-bottom: 0.5ex; - color: white; - background-color: darkblue; - } - .Footer { - font-family: Arial, Helvetica, sans-serif; - font-size: smaller; - } - .ToC { - font-family: Arial, Verdana, Tahoma, Helvetica, sans-serif; - font-size: smaller; - } - span.name { - font-weight: bold; - } - span.filename { - font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif; - font-size: smaller; - } - pre.py { - color: darkblue; - background-color: #EEE; - padding-left: 2.5em; - } - span.typed { - font-weight: bold; - } - - /*** Configuration documentation ***/ - - dl.config { - } - dt.config { - } - dd.config { - } - span.setting { - font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif; - font-size: smaller; - font-weight: bolder; - } - ---> -</style> diff --git a/paste/webkit/FakeWebware/TaskKit/Docs/TODO-TaskKit.text b/paste/webkit/FakeWebware/TaskKit/Docs/TODO-TaskKit.text deleted file mode 100644 index 2e5acd0..0000000 --- a/paste/webkit/FakeWebware/TaskKit/Docs/TODO-TaskKit.text +++ /dev/null @@ -1,43 +0,0 @@ -TaskKit -Webware for Python -TO DO - - -<misc> -<tests> -<docs> - - -<misc> - - [ ] 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()? - -</misc> - - -<tests> - - [ ] make tests that cover all methods and features - -</tests> - - - -<docs> - - [ ] 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 - -</docs> diff --git a/paste/webkit/FakeWebware/TaskKit/Docs/UsersGuide.html b/paste/webkit/FakeWebware/TaskKit/Docs/UsersGuide.html deleted file mode 100644 index 017c470..0000000 --- a/paste/webkit/FakeWebware/TaskKit/Docs/UsersGuide.html +++ /dev/null @@ -1,54 +0,0 @@ -<html> - -<head> - <link rel=stylesheet href=StyleSheet.css type=text/css> - <title>TaskKit User's Guide</title> -</head> - -<body> - -<h1>TaskKit User's Guide</h1> -Version <!-- version --> X.Y <!-- /version --> -<br>Webware for Python <!-- version --> X.Y <!-- /version --> - - -<!-- -<a name=TOC><h2>Table of Contents</h2></a> - -<p> @@ TO DO ---> - - -<a name=Synopsis><h2>Synopsis</h2></a> - -<p> 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). - - -<a name=Feedback><h2>Feedback</h2></a> - -<p> <p> You can e-mail <a href=mailto:webware-discuss@lists.sourceforge.net>webware-discuss@lists.sourceforge.net</a> to give feedback, discuss features and get help using TaskKit. - - -<a name=Introduction><h2>Introduction</h2></a> - -<a name=Overview><h3>Overview</h3></a> - -<p> 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. - -<p> <b>The development status of TaskKit is <i>alpha</i>.</b> 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 <a href=https://sourceforge.net/cvs/?group_id=4866>CVS</a>. You should then re-read the latest docs, including this User's Guide and the <a href=TODO-TaskKit.text>TO DO list</a>. - - -<a name=KnownBugs><h2>Known Bugs</h2></a> - -<p> Known bugs and future work in general, are documented in <a href=TODO-TaskKit.text>TO DO</a>. - - -<a name=Credit><h2>Credit</h2></a> - -<p> Authors: Tom Schwaller, Jay Love - - -<p><hr> - -</body> -</html> diff --git a/paste/webkit/FakeWebware/TaskKit/Docs/index.html b/paste/webkit/FakeWebware/TaskKit/Docs/index.html deleted file mode 100644 index bb8da07..0000000 --- a/paste/webkit/FakeWebware/TaskKit/Docs/index.html +++ /dev/null @@ -1,94 +0,0 @@ -<html> -<head> - <title>TaskKit Documentation</title> - <link rel=stylesheet href=StyleSheet.css type=text/css> - <link rel=stylesheet href=GenIndex.css type=text/css> -</head> - -<body> - -<table border=0 align=center width=100%> - <tr> - <td align=center class=TitleBar>TaskKit Documentation</td> - </tr> -</table> -<br> - -<p> -<table align=center border=0 cellpadding=2 cellspacing=2> - -<tr> <td nowrap class=InfoLabel> Name: </td> <td nowrap class=InfoValue> <b>TaskKit</b> </td> - <td valign=top class=InfoLabel> Summary: </td> </tr> -<tr> <td nowrap class=InfoLabel> Version: </td> <td nowrap class=InfoValue> X.Y </td> - <td rowspan=4 valign=top class=InfoSummary> TaskKit provides a framework for the scheduling and management of tasks which can be triggered periodically or at specific times. </td> </tr> -<tr> <td nowrap class=InfoLabel> Status: </td> <td nowrap class=InfoValue> beta </td> </tr> -<tr> <td nowrap class=InfoLabel> Py ver: </td> <td nowrap class=InfoValue> 2.0 </td> </tr> -<tr> <td nowrap class=InfoLabel> Webware: </td> <td nowrap class=InfoValue> X.Y </td> </tr> - -</table> - -<p> - -<table align=center border=0 cellpadding=2 cellspacing=2> - -<tr> - <td nowrap class=InfoLabel> Manuals: </td> - <td nowrap class=InfoLabel> Source: </td> - <td nowrap class=InfoLabel> Release Notes: </td> -</tr> - -<tr> - <td nowrap valign=top class=InfoValue> <a href=QuickStart.html>Quick Start</a> <br> -<a href=UsersGuide.html>User's Guide</a> <br> -<a href=TODO-TaskKit.text>To Do</a> <br> - </td> - <td nowrap valign=top class=InfoValue> - <a href=Source/ClassHier.html class=DocLink>Class hierarchy</a> <br> - <a href=Source/ClassList.html class=DocLink>Class list</a> - </td> - <td nowrap valign=top class=InfoValue> <a href=RelNotes-X.Y.html>X.Y</a> <br> -<a href=RelNotes-0.8.html>0.8</a> <br> -<a href=RelNotes-0.7.html>0.7</a> <br> -<a href=RelNotes-0.1.1.html>0.1.1</a> <br> - </td> -</tr> - -</table> - - -<!-- -<table align=center border=0 cellpadding=4 cellspacing=4> -<tr> - -<td nowrap valign=top class=DocList> -Manuals -<a href=QuickStart.html>Quick Start</a> <br> -<a href=UsersGuide.html>User's Guide</a> <br> -<a href=TODO-TaskKit.text>To Do</a> <br> - -</td> - -<td nowrap valign=top class=DocList> -Source -<br> <a href=Source/ClassHier.html class=DocLink>Class hierarchy</a> -<br> <a href=Source/ClassList.html class=DocLink>Class list</a> -</td> - -<td nowrap valign=top class=DocList> -Release Notes -<a href=RelNotes-X.Y.html>X.Y</a> <br> -<a href=RelNotes-0.8.html>0.8</a> <br> -<a href=RelNotes-0.7.html>0.7</a> <br> -<a href=RelNotes-0.1.1.html>0.1.1</a> <br> - -</td> -</table> - -</td></tr></table> ---> -<hr> -<span class=Footer> -Webware: <a href=http://webware.sourceforge.net>Home</a> <a href=http://sourceforge.net/projects/webware>@ SourceForge</a> -</span> -</body> -</html> diff --git a/paste/webkit/FakeWebware/TaskKit/Properties.py b/paste/webkit/FakeWebware/TaskKit/Properties.py deleted file mode 100644 index 12dfc67..0000000 --- a/paste/webkit/FakeWebware/TaskKit/Properties.py +++ /dev/null @@ -1,15 +0,0 @@ -name = '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.""" diff --git a/paste/webkit/FakeWebware/TaskKit/Scheduler.py b/paste/webkit/FakeWebware/TaskKit/Scheduler.py deleted file mode 100644 index 0e99e1f..0000000 --- a/paste/webkit/FakeWebware/TaskKit/Scheduler.py +++ /dev/null @@ -1,473 +0,0 @@ - -""" -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) diff --git a/paste/webkit/FakeWebware/TaskKit/Task.py b/paste/webkit/FakeWebware/TaskKit/Task.py deleted file mode 100644 index 196dedb..0000000 --- a/paste/webkit/FakeWebware/TaskKit/Task.py +++ /dev/null @@ -1,59 +0,0 @@ -from 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() diff --git a/paste/webkit/FakeWebware/TaskKit/TaskHandler.py b/paste/webkit/FakeWebware/TaskKit/TaskHandler.py deleted file mode 100644 index 51f5f98..0000000 --- a/paste/webkit/FakeWebware/TaskKit/TaskHandler.py +++ /dev/null @@ -1,138 +0,0 @@ - -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 diff --git a/paste/webkit/FakeWebware/TaskKit/Tests/BasicTest.py b/paste/webkit/FakeWebware/TaskKit/Tests/BasicTest.py deleted file mode 100644 index 235be23..0000000 --- a/paste/webkit/FakeWebware/TaskKit/Tests/BasicTest.py +++ /dev/null @@ -1,58 +0,0 @@ -import 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() diff --git a/paste/webkit/FakeWebware/TaskKit/Tests/Test.py b/paste/webkit/FakeWebware/TaskKit/Tests/Test.py deleted file mode 100644 index b45d3f6..0000000 --- a/paste/webkit/FakeWebware/TaskKit/Tests/Test.py +++ /dev/null @@ -1,30 +0,0 @@ -import 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) diff --git a/paste/webkit/FakeWebware/TaskKit/Tests/__init__.py b/paste/webkit/FakeWebware/TaskKit/Tests/__init__.py deleted file mode 100644 index 792d600..0000000 --- a/paste/webkit/FakeWebware/TaskKit/Tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/paste/webkit/FakeWebware/TaskKit/__init__.py b/paste/webkit/FakeWebware/TaskKit/__init__.py deleted file mode 100644 index cffb062..0000000 --- a/paste/webkit/FakeWebware/TaskKit/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ - -def InstallInWebKit(appserver): - pass diff --git a/paste/webkit/FakeWebware/WebKit/Application.py b/paste/webkit/FakeWebware/WebKit/Application.py deleted file mode 100644 index 7e1aef0..0000000 --- a/paste/webkit/FakeWebware/WebKit/Application.py +++ /dev/null @@ -1 +0,0 @@ -from paste.webkit.wkapplication import * diff --git a/paste/webkit/FakeWebware/WebKit/HTTPExceptions.py b/paste/webkit/FakeWebware/WebKit/HTTPExceptions.py deleted file mode 100644 index e64978b..0000000 --- a/paste/webkit/FakeWebware/WebKit/HTTPExceptions.py +++ /dev/null @@ -1,8 +0,0 @@ -from 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) diff --git a/paste/webkit/FakeWebware/WebKit/HTTPServlet.py b/paste/webkit/FakeWebware/WebKit/HTTPServlet.py deleted file mode 100644 index 104d2ec..0000000 --- a/paste/webkit/FakeWebware/WebKit/HTTPServlet.py +++ /dev/null @@ -1 +0,0 @@ -from paste.webkit.wkservlet import HTTPServlet diff --git a/paste/webkit/FakeWebware/WebKit/Page.py b/paste/webkit/FakeWebware/WebKit/Page.py deleted file mode 100644 index 2bb01ed..0000000 --- a/paste/webkit/FakeWebware/WebKit/Page.py +++ /dev/null @@ -1 +0,0 @@ -from paste.webkit.wkservlet import Page diff --git a/paste/webkit/FakeWebware/WebKit/__init__.py b/paste/webkit/FakeWebware/WebKit/__init__.py deleted file mode 100644 index 792d600..0000000 --- a/paste/webkit/FakeWebware/WebKit/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/paste/webkit/FakeWebware/WebUtils/Funcs.py b/paste/webkit/FakeWebware/WebUtils/Funcs.py deleted file mode 100644 index d467257..0000000 --- a/paste/webkit/FakeWebware/WebUtils/Funcs.py +++ /dev/null @@ -1,148 +0,0 @@ -""" -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', '<br>'] -] - -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 <p>. 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 <table> where each row is a key-value pair. """ - keys = dict.keys() - keys.sort() - # A really great (er, bad) example of hardcoding. :-) - html = ['<table width=100% border=0 cellpadding=2 cellspacing=2>'] - 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('<tr bgcolor=#F0F0F0> <td> %s </td> <td> %s </td> </tr>\n' % (htmlEncode(str(key)), htmlEncode(value))) - html.append('</table>') - 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) diff --git a/paste/webkit/FakeWebware/WebUtils/__init__.py b/paste/webkit/FakeWebware/WebUtils/__init__.py deleted file mode 100644 index 792d600..0000000 --- a/paste/webkit/FakeWebware/WebUtils/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/paste/webkit/FakeWebware/__init__.py b/paste/webkit/FakeWebware/__init__.py deleted file mode 100644 index 792d600..0000000 --- a/paste/webkit/FakeWebware/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/paste/webkit/__init__.py b/paste/webkit/__init__.py deleted file mode 100644 index 792d600..0000000 --- a/paste/webkit/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/paste/webkit/conf_setup.py b/paste/webkit/conf_setup.py deleted file mode 100644 index cf34457..0000000 --- a/paste/webkit/conf_setup.py +++ /dev/null @@ -1,33 +0,0 @@ -import sys -import os -from paste import makeapp -from paste import urlparser -from paste import session -from paste import recursive -from paste import httpexceptions -from paste.util.findpackage import find_package - -def build_application(conf): - if not 'publish_dir' in conf: - print 'You must provide a publish_dir configuration value' - sys.exit(2) - directory = conf['publish_dir'] - install_fake_webware = conf.get('install_fake_webware', True) - if install_fake_webware: - _install_fake_webware() - package = find_package(directory) - app = urlparser.URLParser(directory, package) - app = makeapp.apply_conf_middleware( - app, conf, - [httpexceptions.middleware, session.SessionMiddleware, - recursive.RecursiveMiddleware]) - app = makeapp.apply_default_middleware(app, conf) - return app - -def install_fake_webware(): - fake_webware_dir = os.path.join(os.path.dirname(__file__), - 'FakeWebware') - if fake_webware_dir in sys.path: - return - sys.path.insert(0, fake_webware_dir) -_install_fake_webware = install_fake_webware diff --git a/paste/webkit/examples/EchoServlet.py b/paste/webkit/examples/EchoServlet.py deleted file mode 100644 index 6dc30c6..0000000 --- a/paste/webkit/examples/EchoServlet.py +++ /dev/null @@ -1,57 +0,0 @@ -r"""\ -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!') diff --git a/paste/webkit/examples/__init__.py b/paste/webkit/examples/__init__.py deleted file mode 100644 index 792d600..0000000 --- a/paste/webkit/examples/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/paste/webkit/wkapplication.py b/paste/webkit/wkapplication.py deleted file mode 100644 index 33d3f84..0000000 --- a/paste/webkit/wkapplication.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -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() diff --git a/paste/webkit/wkcommon.py b/paste/webkit/wkcommon.py deleted file mode 100644 index 7203153..0000000 --- a/paste/webkit/wkcommon.py +++ /dev/null @@ -1,190 +0,0 @@ -import 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() diff --git a/paste/webkit/wkrequest.py b/paste/webkit/wkrequest.py deleted file mode 100644 index 5de66cf..0000000 --- a/paste/webkit/wkrequest.py +++ /dev/null @@ -1,347 +0,0 @@ -""" -A Webware HTTPRequest object, implemented based on the WSGI request -environment dictionary. -""" - -import time -import traceback -import cgi -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._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): - base = self._environ['paste.config']['publish_dir'] - 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 diff --git a/paste/webkit/wkresponse.py b/paste/webkit/wkresponse.py deleted file mode 100644 index 2738934..0000000 --- a/paste/webkit/wkresponse.py +++ /dev/null @@ -1,273 +0,0 @@ -""" -A Webware HTTPResponse object. -""" - -import time -from wkcommon import NoDefault, Cookie -from types import * -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 type(t) is StringType and t and t[0] == '+': - interval = timeDecode(t[1:]) - t = time.time() + interval - if type(t) in (IntType, LongType,FloatType): - t = time.gmtime(t) - if type(t) in (TupleType, 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 - - assert self._committed==0, "Headers already sent" - - self.setHeader('Status', '302 Redirect') - self.setHeader('Location', url) - self.setHeader('Content-type', 'text/html') - - self.write('<html> <body> This page has been redirected to ' - '<a href="%s">%s</a>. </body> </html>' % (url, url)) - - ## Output ## - - def write(self, charstr=None): - """ - Write charstr to the response stream. - """ - import pdb - 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 diff --git a/paste/webkit/wkservlet.py b/paste/webkit/wkservlet.py deleted file mode 100644 index ba72fbd..0000000 --- a/paste/webkit/wkservlet.py +++ /dev/null @@ -1,477 +0,0 @@ -""" -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 - -class make_application(object): - - def __get__(self, obj, type=None): - # Instances are already applications: - if obj: - return obj - # This application creates an instance for each call: - def application(environ, start_response): - return type()(environ, start_response) - return application - -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 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? - class ReturnIterException(Exception): - def __init__(self, app_iter): - self.app_iter = app_iter - - 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() - - wsgi_app = make_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 AbstractError, self.__class__ - - 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 <body> 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('<html>') - self.writeHead() - self.writeBody() - self.writeln('</html>') - - def writeDocType(self): - """ - Invoked by writeHTML() to write the <!DOCTYPE ...> 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('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">') - pass - - def writeHead(self): - """ - Writes the <head> portion of the page by writing the - <head>...</head> tags and invoking writeHeadParts() in between. - """ - wr = self.writeln - wr('<head>') - self.writeHeadParts() - wr('</head>') - - def writeHeadParts(self): - """ - Writes the parts inside the <head>...</head> 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 <title> portion of the page. Uses title(). - """ - self.writeln('\t<title>%s</title>' % 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<link rel=stylesheet href=StyleSheet.css type=text/css>') - """ - pass - - def writeBody(self): - """ - Writes the <body> portion of the page by writing the - <body>...</body> (making use of self.htBodyArgs()) and invoking - self.writeBodyParts() in between. - """ - wr = self.writeln - bodyArgs = self.htBodyArgs() - if bodyArgs: - wr('<body %s>' % bodyArgs) - else: - wr('<body>') - self.writeBodyParts() - wr('</body>') - - 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('<p> This page has not yet customized its content. </p>') - - - ## 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='</body></html>'): - raise NotImplementedError - - diff --git a/paste/webkit/wksession.py b/paste/webkit/wksession.py deleted file mode 100644 index 1cbdd71..0000000 --- a/paste/webkit/wksession.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -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) - - diff --git a/paste/webkit/wktransaction.py b/paste/webkit/wktransaction.py deleted file mode 100644 index ec084d5..0000000 --- a/paste/webkit/wktransaction.py +++ /dev/null @@ -1,98 +0,0 @@ -""" -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): - return self._session is not None - - 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 awake(self): - __traceback_hide__ = True - if self._session: - self._session.awake(self) - self._servlet.awake(self) - - def respond(self): - self._servlet.respond(self) - - def sleep(self): - self._servlet.sleep(self) - - 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 - try: - self.awake() - self.respond() - finally: - self.sleep() - - 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) - |