[Zope-CVS] CVS: Products/Ape/lib/apelib/fs - __init__.py:1.1 cache.py:1.1 classification.py:1.1 connection.py:1.1 exceptions.py:1.1 interfaces.py:1.1 params.py:1.1 properties.py:1.1 security.py:1.1 structure.py:1.1
Shane Hathaway
shane@zope.com
Wed, 9 Apr 2003 23:09:57 -0400
Update of /cvs-repository/Products/Ape/lib/apelib/fs
In directory cvs.zope.org:/tmp/cvs-serv32010/lib/apelib/fs
Added Files:
__init__.py cache.py classification.py connection.py
exceptions.py interfaces.py params.py properties.py
security.py structure.py
Log Message:
Moved apelib into a "lib" subdirectory. This simplified the
Python hacking required to make apelib a top-level package. Sorry
about the flood of checkins, but CVS makes a move like this quite painful.
=== Added File Products/Ape/lib/apelib/fs/__init__.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Filesystem gateway package.
$Id: __init__.py,v 1.1 2003/04/10 03:09:55 shane Exp $
"""
=== Added File Products/Ape/lib/apelib/fs/cache.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Simple short-lived object cache.
$Id: cache.py,v 1.1 2003/04/10 03:09:55 shane Exp $
"""
from time import time
class ShortLivedCache:
def __init__(self, lifetime=1):
# The default lifetime is 1 second.
self.lifetime = lifetime
self.data = {}
self.expiration = time() + lifetime
def get(self, key, default=None):
now = time()
if now >= self.expiration:
self.data.clear()
return default
res = self.data.get(key, default)
return res
def set(self, key, value):
now = time()
if now >= self.expiration:
self.data.clear()
self.expiration = now + self.lifetime
self.data[key] = value
def invalidate(self, key):
try:
del self.data[key]
except KeyError:
pass
def clear(self):
self.data.clear()
=== Added File Products/Ape/lib/apelib/fs/classification.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Filesystem classification section.
$Id: classification.py,v 1.1 2003/04/10 03:09:55 shane Exp $
"""
from apelib.core.interfaces import IGateway
from apelib.core.schemas import FieldSchema
class FSClassificationSection:
"""Gateway for storing classification data."""
__implements__ = IGateway
schema = FieldSchema('classification', 'classification')
def __init__(self, fs_conn):
self.fs_conn = fs_conn
def getSchema(self):
return self.schema
def load(self, event):
c = self.fs_conn
p = event.getKeychain()[-1]
classification = {'node_type': c.readNodeType(p)}
text = c.readSection(p, 'classification', '')
if text:
lines = text.split('\n')
for line in lines:
if '=' in line:
k, v = line.split('=', 1)
classification[k.strip()] = v.strip()
classification['extension'] = c.getExtension(p)
return classification, text.strip()
def store(self, event, state):
# state is a classification
p = event.getKeychain()[-1]
items = state.items()
items.sort()
text = []
for k, v in items:
if k == 'extension':
self.fs_conn.suggestExtension(p, v)
else:
text.append('%s=%s' % (k, v))
text = '\n'.join(text)
self.fs_conn.writeSection(p, 'classification', text)
return text.strip()
=== Added File Products/Ape/lib/apelib/fs/connection.py === (486/586 lines abridged)
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Full-featured Filesystem connection class.
$Id: connection.py,v 1.1 2003/04/10 03:09:55 shane Exp $
"""
import os
import re
from shutil import rmtree
from types import StringType
from apelib.core.interfaces import ITPCConnection
from apelib.core.exceptions import NoStateFoundError
from interfaces import IFSConnection
from exceptions import FSWriteError
from cache import ShortLivedCache
# Try to decipher this regular expression ;-)
# It basically matches "\n[sectionname]...\n", where len(sectionname) > 0.
section_re = re.compile(r'^\[([^\[\]\n]+)\][^\r\n]*(?:\r\n|\r|\n)',
re.MULTILINE)
# For a NODE_TYPE_SECTION, the value is 'f' (file) or 'd' (directory)
NODE_TYPE_SECTION = '@node_type'
# For a DATA_SECTION, the value is a two-item tuple containing a
# string (file) or list of names (directory) and the as_text flag.
DATA_SECTION = '@data'
SUGGESTED_EXTENSION_SECTION = '@s_ext' # The suggested filename extension.
OBJECT_NAMES_SECTION = 'object_names' # For directories. The value is text.
REMAINDER_SECTION = 'remainder' # The value is a binary string.
PROPERTIES_EXTENSION = 'properties'
REMAINDER_EXTENSION = 'remainder'
# Match 'foo.properties', 'foo.remainder', 'properties', or 'remainder'.
[-=- -=- -=- 486 lines omitted -=- -=- -=-]
#
# ITPCConnection implementation
#
def sortKey(self):
return self.basepath
def getName(self):
return self.basepath
def connect(self):
if not os.path.exists(self.basepath):
os.makedirs(self.basepath)
def begin(self):
self._props_cache.clear()
self._dir_cache.clear()
def vote(self):
"""Do some early verification
This is done while the transaction can still be vetoed safely.
"""
items = self._pending.items()
items.sort() # Ensure that base directories come first.
self._beforeWrite(items)
self._final = 1
def reset(self):
self._final = 0
self._pending.clear()
self._props_cache.clear()
self._dir_cache.clear()
def abort(self):
self.reset()
def finish(self):
if self._final:
try:
items = self._pending.items()
items.sort() # Ensure that base directories come first.
for subpath, sections in items:
self._writeFinal(subpath, sections)
finally:
self.reset()
def close(self):
self.reset()
=== Added File Products/Ape/lib/apelib/fs/exceptions.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""fs_gateway exception types
$Id: exceptions.py,v 1.1 2003/04/10 03:09:55 shane Exp $
"""
class FSWriteError (Exception):
"""Unable to write data"""
=== Added File Products/Ape/lib/apelib/fs/interfaces.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Filesystem-specific interfaces
$Id: interfaces.py,v 1.1 2003/04/10 03:09:55 shane Exp $
"""
from Interface import Interface
class IFSConnection (Interface):
"""Simple filesystem connection (with textual annotations).
"""
def writeSection(subpath, section_name, data):
"""Writes a text-based metadata section for a filesystem node."""
def writeNodeType(subpath, data):
"""Writes the node type for a filesystem node.
'd' (directory) and 'f' (file) are supported.
"""
def writeData(subpath, data, as_text=0):
"""Writes data to a filesystem node.
In the case of directories, expects a tuple containing the names
of the files that should be in the directory. In the case of
files, expects a string.
If as_text is true, the file is written in text mode. The
as_text flag is ignored for directories.
"""
def suggestExtension(subpath, ext):
"""Suggests a filename extension for a filesystem node.
The IFSConnection may use this information to store the file
with an automatically appended filename extension.
"""
def readSection(subpath, section_name, default=None):
"""Reads a text-based metadata section.
"""
def readNodeType(subpath):
"""Reads the node type of a filesystem node.
"""
def readData(subpath, allow_missing=0, as_text=0):
"""Reads the data from a filesystem node.
For files, this reads the main data stream. For directories,
this returns a list of names. If the allow_missing flag is
specified, this method returns None if no filesystem node is
found.
If as_text is true, the file is read in text mode. The
as_text flag is ignored for directories.
"""
def getExtension(subpath):
"""Returns the filename extension used for a filesystem node.
"""
def getModTime(subpath, default=0):
"""Returns the modification time of a file.
"""
=== Added File Products/Ape/lib/apelib/fs/params.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Functions for encoding/decoding parameter strings.
$Id: params.py,v 1.1 2003/04/10 03:09:55 shane Exp $
"""
import re
token_re = re.compile(r'([" \t]|\\["\\trn])')
token_replacements = {
'\\"': '"',
'\\\\': '\\',
'\\t': '\t',
'\\r': '\r',
'\\n': '\n',
}
key_re = re.compile(r'[A-Za-z_-][A-Za-z0-9_-]*$')
def splitParams(s):
tokens = re.split(token_re, s)
params = []
param = []
quoting = 0
for tok in tokens:
if tok:
v = token_replacements.get(tok)
if v:
param.append(v)
elif not quoting and (tok == ' ' or tok == '\t'):
if param:
params.append(''.join(param))
param = []
else:
if tok == '"':
quoting = not quoting
if not quoting:
params.append(''.join(param))
param = []
else:
param.append(tok)
leftover = ''.join(param).strip()
if leftover:
params.append(leftover)
return params
def escapeParam(s):
return s.replace('\\', '\\\\').replace('"', '\\"').replace(
'\r', '\\r').replace('\n', '\\n').replace('\t', '\\t')
def stringToParams(s):
"""Decodes a string of the format 'a="..." b="..."'.
Returns a list of (key, value) pairs.
"""
params = splitParams(s)
res = []
for param in params:
p = param.split('=', 1)
if len(p) == 1:
k = p[0]
v = ''
else:
k, v = p
res.append((k, v))
return res
def paramsToString(params):
"""Encodes a list of (key, value) pairs as a string."""
parts = []
for k, v in params:
if not key_re.match(k):
raise ValueError, 'Bad parameter name: %s' % repr(k)
if v:
parts.append('%s="%s"' % (k, escapeParam(v)))
else:
parts.append(k)
return ' '.join(parts)
=== Added File Products/Ape/lib/apelib/fs/properties.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Filesystem property gateways.
$Id: properties.py,v 1.1 2003/04/10 03:09:55 shane Exp $
"""
from types import StringType
from apelib.core.interfaces import IGateway
from apelib.core.schemas import FieldSchema, RowSequenceSchema
token_replacements = {
'\\\\': '\\',
'\\t': '\t',
'\\r': '\r',
'\\n': '\n',
}
def escape_string(s):
return s.replace('\\', '\\\\').replace('\n', '\\n').replace(
'\r', '\\r').replace('\t', '\\t')
def unescape_string(s):
res = []
pos = 0
while 1:
p = s.find('\\', pos)
if p >= 0:
res.append(s[pos:p])
token = s[p:p+2]
c = token_replacements.get(token)
if c is not None:
# known escape sequence
res.append(c)
else:
# unknown sequence
res.append(token)
pos = p + 2
else:
res.append(s[pos:])
break
return ''.join(res)
class FSProperties:
"""Simple properties to filesystem property section gateway."""
__implements__ = IGateway
schema = RowSequenceSchema()
schema.addField('id', 'string', 1)
schema.addField('type', 'string')
schema.addField('data', 'string')
def __init__(self, fs_conn, section='properties'):
self.fs_conn = fs_conn
self.section = section
def getSchema(self):
return self.schema
def load(self, event):
p = event.getKeychain()[-1]
text = self.fs_conn.readSection(p, self.section, '')
res = []
if text:
lines = text.split('\n')
for line in lines:
if '=' in line:
k, v = line.split('=', 1)
if ':' in k:
k, t = k.split(':', 1)
else:
t = 'string'
res.append((k, t, unescape_string(v)))
res.sort()
return res, tuple(res)
def store(self, event, state):
lines = []
for k, t, v in state:
lines.append('%s:%s=%s' % (k, t, escape_string(v)))
lines.sort()
text = '\n'.join(lines)
p = event.getKeychain()[-1]
self.fs_conn.writeSection(p, self.section, text)
state = list(state)
state.sort()
return tuple(state)
class FSSectionData:
"""Text to filesystem property section gateway."""
__implements__ = IGateway
schema = FieldSchema('data', 'string')
def __init__(self, fs_conn, section):
self.fs_conn = fs_conn
self.section = section
def getSchema(self):
return self.schema
def load(self, event):
p = event.getKey()
state = self.fs_conn.readSection(p, self.section, '').strip()
return state, state
def store(self, event, state):
if not isinstance(state, StringType):
raise RuntimeError('Not a string: %s' % repr(state))
state = state.strip()
if state:
p = event.getKey()
self.fs_conn.writeSection(p, self.section, state)
return state
=== Added File Products/Ape/lib/apelib/fs/security.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Gateways for storing security information.
$Id: security.py,v 1.1 2003/04/10 03:09:55 shane Exp $
"""
from apelib.core.interfaces import IGateway
from apelib.core.schemas import RowSequenceSchema
from apelib.core.exceptions import MappingError
from params import stringToParams, paramsToString
class FSSecurityAttributes:
"""Gateway for storing security attributes."""
__implements__ = IGateway
schema = RowSequenceSchema()
schema.addField('declaration_type', 'string')
schema.addField('role', 'string')
schema.addField('permission', 'string')
schema.addField('username', 'string')
def __init__(self, fs_conn, section='security'):
self.fs_conn = fs_conn
self.section = section
def getSchema(self):
return self.schema
def load(self, event):
key = event.getKey()
text = self.fs_conn.readSection(key, self.section, '')
res = []
if text:
lines = text.split('\n')
for line in lines:
line = line.strip()
if not line or line.startswith('#'):
continue
params = stringToParams(line)
if params:
decl_type = params[0][0]
row = [decl_type, '', '', '']
for k, v in params[1:]:
k = k.lower()
if '_' in k:
# temporary backward compatibility
k = k.split('_', 1)[0]
if k == 'role':
row[1] = v
elif k == 'permission':
row[2] = v
elif k == 'username':
row[3] = v
else:
raise ValueError(
"Could not read security declaration "
"%s for %s" % (repr(line), repr(key)))
res.append(tuple(row))
res.sort()
return res, tuple(res)
def store(self, event, state):
lines = []
for d, r, p, u in state:
params = [(d, '')]
if r:
params.append(('role', r))
if p:
params.append(('permission', p))
if u:
params.append(('username', u))
s = paramsToString(params)
lines.append(s)
if lines:
lines.sort()
text = '\n'.join(lines)
self.fs_conn.writeSection(event.getKey(), self.section, text)
state = list(state)
state.sort()
return tuple(state)
class FSUserList:
"""User list gateway, where the user list is stored in a flat file."""
__implements__ = IGateway
schema = RowSequenceSchema()
schema.addField('id', 'string', 1)
schema.addField('password', 'string')
schema.addField('roles', 'string:list')
schema.addField('domains', 'string:list')
def __init__(self, fs_conn):
self.fs_conn = fs_conn
def getSchema(self):
return self.schema
def load(self, event):
c = self.fs_conn
p = event.getKeychain()[-1]
assert c.readNodeType(p) == 'f'
text = c.readData(p)
res = []
for line in text.split('\n'):
L = line.strip()
if not L.startswith('#') and ':' in L:
id, password, rolelist, domainlist = L.split(':', 3)
roles = self._splitList(rolelist)
domains = self._splitList(domainlist)
res.append((id, password, roles, domains))
res.sort()
return res, text
def _splitList(self, s):
return tuple([item.strip() for item in s.split(',') if item])
def _joinList(self, items):
for item in items:
if item.strip() != item:
raise MappingError(
"Leading and trailing whitespace are not allowed "
"in roles and domains")
item = item.strip()
if not item:
raise MappingError("Empty role or domain not allowed")
if ',' in item or ':' in item or '\n' in item:
raise MappingError(
"Commas, colons, and newlines are not allowed "
"in roles and domains")
return ','.join(items)
def store(self, event, state):
replace_lines = {}
for id, password, roles, domains in state:
if ':' in id or '\n' in id:
raise MappingError('User IDs cannot have colons or newlines')
if id.startswith('#'):
raise MappingError('User IDs cannot start with #')
if ':' in password or '\n' in password:
raise MappingError('Passwords cannot have colons or newlines')
rolelist = self._joinList(roles)
domainlist = self._joinList(domains)
to_write = '%s:%s:%s:%s' % (id, password, rolelist, domainlist)
replace_lines[id] = to_write
p = event.getKeychain()[-1]
self.fs_conn.writeNodeType(p, 'f')
text = self.fs_conn.readData(p, allow_missing=1)
if text is None:
text = ''
new_lines = []
# Replace / remove users
for line in text.split('\n'):
L = line.strip()
if not L.startswith('#'):
if ':' in L:
name, stuff = L.split(':', 1)
replace = replace_lines.get(name, '')
if replace and replace != L:
new_lines.append(replace)
del replace_lines[name]
# else remove the line
else:
new_lines.append(line)
# Append new users
for line in replace_lines.values():
new_lines.append(line)
# Write it
text = '\n'.join(new_lines)
self.fs_conn.writeData(p, text)
serial = list(state)
serial.sort()
return text
=== Added File Products/Ape/lib/apelib/fs/structure.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Basic filesystem gateways.
$Id: structure.py,v 1.1 2003/04/10 03:09:55 shane Exp $
"""
from types import StringType
from apelib.core.interfaces import IGateway
from apelib.core.schemas import FieldSchema, RowSequenceSchema
class FSFileData:
"""File data gateway, where data is a string.
"""
__implements__ = IGateway
schema = FieldSchema('data', 'string')
def __init__(self, fs_conn, text=0):
self.fs_conn = fs_conn
self.text = text
def getSchema(self):
return self.schema
def load(self, event):
c = self.fs_conn
p = event.getKeychain()[-1]
assert c.readNodeType(p) == 'f'
state = c.readData(p, as_text=self.text)
return state, state
def store(self, event, state):
if not isinstance(state, StringType):
raise RuntimeError('Not a string: %s' % repr(state))
c = self.fs_conn
p = event.getKeychain()[-1]
c.writeNodeType(p, 'f')
c.writeData(p, state, as_text=self.text)
return state
class FSAutoId:
"""Automatic ID gateway based on the keychain of the item."""
__implements__ = IGateway
schema = FieldSchema('id', 'string')
def getSchema(self):
return self.schema
def getIdFrom(self, event):
path = event.getKeychain()[-1]
pos = path.rfind('/')
if pos >= 0:
return path[pos + 1:]
else:
return path
def load(self, event):
id = self.getIdFrom(event)
return id, id
def store(self, event, state):
id = self.getIdFrom(event)
if state != id:
raise ValueError('Mismatched file ID')
return id
class FSDirectoryItems:
"""Read/write objects in a filesystem directory."""
__implements__ = IGateway
schema = RowSequenceSchema()
schema.addField('id', 'string', 1)
def __init__(self, fs_conn):
self.fs_conn = fs_conn
def getSchema(self):
return self.schema
def load(self, event):
p = event.getKeychain()[-1]
c = self.fs_conn
assert c.readNodeType(p) == 'd'
names = c.readData(p)
names.sort()
res = tuple([(name,) for name in names])
return res, res
def store(self, event, state):
p = event.getKeychain()[-1]
c = self.fs_conn
c.writeNodeType(p, 'd')
state = list(state)
state.sort()
names = [row[0] for row in state]
c.writeData(p, names)
return tuple(state)
class FSModTime:
"""Reads the modification time of a file."""
__implements__ = IGateway
schema = FieldSchema('mtime', 'int')
def __init__(self, fs_conn):
self.fs_conn = fs_conn
def getSchema(self):
return self.schema
def load(self, event):
p = event.getKey()
state = long(self.fs_conn.getModTime(p))
return state, None # Use None as the hash (see store())
def store(self, event, state):
# Under normal circumstances, there is no need to change the mod
# time of a file. Ignore by returning None as the hash.
return None