[Zope3-checkins] CVS: Zope3/src/zope/app/schemagen - __init__.py:1.1.2.1 modulegen.py:1.1.2.1 schemaspec.py:1.1.2.1 typereg.py:1.1.2.1
Jim Fulton
jim@zope.com
Mon, 23 Dec 2002 14:32:15 -0500
Update of /cvs-repository/Zope3/src/zope/app/schemagen
In directory cvs.zope.org:/tmp/cvs-serv19908/zope/app/schemagen
Added Files:
Tag: NameGeddon-branch
__init__.py modulegen.py schemaspec.py typereg.py
Log Message:
Initial renaming before debugging
=== Added File Zope3/src/zope/app/schemagen/__init__.py ===
#
# This file is necessary to make this directory a package.
=== Added File Zope3/src/zope/app/schemagen/modulegen.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.
#
##############################################################################
"""
$Id: modulegen.py,v 1.1.2.1 2002/12/23 19:32:13 jim Exp $
"""
from zope.app.schemagen.typereg import fieldRegistry
def generateModuleSource(schema_name, fields, class_name,
schema_version=0, extra_imports='', extra_methods=''):
"""Generate module source from schema information.
extra_methods if supplied needs to be a string with a four space indent.
"""
if extra_methods:
extra_methods = '\n%s' % extra_methods
if extra_imports:
extra_imports = '%s\n' % extra_imports
import_list = []
field_text_list = []
property_text_list = []
for field_name, field in fields:
r = fieldRegistry.represent(field)
field_text_list.append(' %s = %s' % (field_name, r.text))
property_text_list.append(
" %s = "
"FieldProperty(%s['%s'])" % (
field_name, schema_name, field_name))
for import_entry in r.importList:
import_list.append("from %s import %s" % import_entry)
import_text = '\n'.join(import_list)
field_text = '\n'.join(field_text_list)
property_text = '\n'.join(property_text_list)
text = '''\
from zope.interface import Interface
from persistence import Persistent
from zope.schema.fieldproperty import FieldProperty
# field imports
%(import_text)s
%(extra_imports)s
class %(schema_name)s(Interface):
"""Autogenerated schema."""
%(field_text)s
class %(class_name)s(Persistent):
"""Autogenerated class for %(schema_name)s."""
__implements__ = %(schema_name)s
def __init__(self):
self.__schema_version__ = %(schema_version)s
%(property_text)s
%(extra_methods)s
''' % vars()
return text
=== Added File Zope3/src/zope/app/schemagen/schemaspec.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.
#
##############################################################################
"""
$Id: schemaspec.py,v 1.1.2.1 2002/12/23 19:32:13 jim Exp $
"""
__metaclass__ = type
from zope.app.interfaces.schemagen import ISchemaSpec
from persistence import Persistent
from zope.app.schemagen.modulegen import generateModuleSource
_helper_import = 'from Zope.App.schemagen import schemaspec'
_helper_module = 'schemaspec'
class SchemaSpec(Persistent):
__implements__ = ISchemaSpec
def __init__(self, schema_name, class_name=None):
if class_name is None:
class_name = schema_name + 'Class'
if class_name.startswith('I'):
class_name = class_name[1:]
self._schema_name = schema_name
self._class_name = class_name
self._fields = {}
self._field_names = []
self._current_version = 0
self._history = []
def _appendHistory(self, action):
self._history.append(action)
self._current_version += 1
def addField(self, name, field):
if name in self._fields:
raise KeyError, "Field %s already exists." % name
# XXX should check name is a sensible pythonic name
self._field_names.append(name)
self._fields[name] = field
action = AddField(name)
self._appendHistory(action)
self._p_changed = 1
return action
def removeField(self, name):
if name not in self._fields:
raise KeyError, "Field %s does not exist." % name
del self._fields[name]
self._field_names.remove(name)
action = RemoveField(name)
self._appendHistory(action)
self._p_changed = 1
return action
def renameField(self, orig_name, target_name):
if orig_name not in self._fields:
raise KeyError, "Field %s does not exist." % orig_name
if target_name in self._fields:
raise KeyError, "Field %s already exists." % target_name
# XXX should check target_name is pythonic
position = self._field_names.index(orig_name)
self._field_names[position] = target_name
self._fields[target_name] = self._fields[orig_name]
del self._fields[orig_name]
action = RenameField(orig_name, target_name)
self._appendHistory(action)
self._p_changed = 1
return action
def insertField(self, name, field, position):
if name in self._fields:
raise KeyError, "Field %s already exists." % name
if not 0 <= position <= len(self._field_names):
raise IndexError, "Position %s out of range." % name
# XXX should check name is pythonic
self._fields[name] = field
self._field_names.insert(position, name)
action = InsertField(name, position)
self._appendHistory(action)
self._p_changed = 1
return action
def moveField(self, name, position):
if name not in self._fields:
raise KeyError, "Field %s does not exist." % name
if not 0 <= position <= len(self._field_names):
raise IndexError, "Position %s out of range." % name
self._field_names.remove(name)
self._field_names.insert(position, name)
action = MoveField(name, position)
self._appendHistory(action)
self._p_changed = 1
return action
def getFieldsInOrder(self):
"""Get all fields in order as (name, field) tuples.
"""
return [(field_name, self._fields[field_name])
for field_name in self._field_names]
def getCurrentVersion(self):
return self._current_version
def getHistory(self):
return self._history
def generateSetstateSource(self):
lines = [
'transformations = %s.prepareSetstate(self, state, %r)' % (
_helper_module, self._current_version),
'if transformations is None:',
' return',
'dict = self.__dict__'
]
count = self._current_version - len(self._history)
for item in self._history:
args = ', '.join(map(repr, item.code()))
if args:
args = ', '+args
lines.append('if %s in transformations:' % count)
lines.append(' %s.%s.update(dict, state%s)' % (
_helper_module, type(item).__name__, args))
count += 1
method_text_list = [' def __setstate__(self, state):']
# indent by 8: '12345678%s'
method_text_list += [' %s' % line for line in lines]
return '\n'.join(method_text_list) + '\n'
def generateModuleSource(self):
if not self._history:
# don't generate any __setstate__ when there is no history
# to update from
return generateModuleSource(
self._schema_name, self.getFieldsInOrder(),
self._class_name, schema_version=self._current_version)
else:
return generateModuleSource(
self._schema_name, self.getFieldsInOrder(),
self._class_name,
schema_version=self._current_version,
extra_imports=_helper_import,
extra_methods=self.generateSetstateSource())
# future plans, perhaps:
# make each of these classes have views, and a method that
# returns a clear explanation of the consequences of this change
# for instances of the schema class.
# Make the history viewable by schema authors.
# Make individual items in the history turn-off-and-on-able
# so that you can choose to treat an add.... remove-add as a no-op,
# and preserve the state of instances.
class AddField:
def __init__(self, name):
self.name = name
def code(self):
return (self.name,)
def update(dict, state, name):
# clear the way for any default value if schema changes are
# undone
try:
del dict[name]
except KeyError:
pass
update = staticmethod(update)
class RemoveField:
def __init__(self, name):
self.name = name
def code(self):
return (self.name,)
def update(dict, state, name):
del dict[name]
update = staticmethod(update)
class RenameField:
def __init__(self, original, target):
self.original = original
self.target = target
def code(self):
return self.original, self.target
def update(dict, state, original, target):
del dict[original]
dict[target] = state[original]
update = staticmethod(update)
class InsertField:
def __init__(self, name, position):
self.name = name
self.position = position
def code(self):
return self.name, self.position
def update(dict, state, name, position):
pass
update = staticmethod(update)
class MoveField:
def __init__(self, name, position):
self.name = name
self.position = position
def code(self):
return self.name, self.position
def update(dict, state, name, position):
pass
update = staticmethod(update)
def prepareSetstate(obj, state, schema_version):
obj.__dict__.update(state)
state_version = state['__schema_version__']
if schema_version == state_version:
return None
assert state_version < schema_version
obj.__schema_version__ = schema_version
d = {}
for i in range(state_version, schema_version):
d[i] = 1
return d
=== Added File Zope3/src/zope/app/schemagen/typereg.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.
#
##############################################################################
"""XXX short summary goes here.
XXX longer description goes here.
$Id: typereg.py,v 1.1.2.1 2002/12/23 19:32:13 jim Exp $
"""
from zope.interface.implements import visitImplements
from zope.schema import getFields
from zope.app.interfaces.schemagen import ITypeRepresentation
class TypeRepresentationRegistry:
def __init__(self, default):
self._registry = {}
self._default = default
#def registerType(self, type, factory):
# self._registry[type] = factory
def represent(self, object):
# returns an ITypeRepresentation object
factory = self._registry.get(type(object))
if factory is not None:
return factory(object)
return self._default(object)
def register(self, representation):
for type in representation.getTypes():
self._registry[type] = representation
class DefaultTypeRepresentation:
__implements__ = ITypeRepresentation
def __init__(self, object):
self.text = repr(object)
import_name = type(object).__name__
import_module = type(object).__module__
if import_module != '__builtin__':
self.importList = [(import_module, import_name)]
else:
self.importList = []
def getTypes():
return ()
getTypes = staticmethod(getTypes)
class DatetimeRepresentation:
__implements__ = ITypeRepresentation
def __init__(self, dt):
r = repr(dt)
assert r.startswith('datetime.')
self.text = r[9:]
self.importList = [('datetime', type(dt).__name__)]
def getTypes():
import datetime
# XXX not supporting tz or timedelta yet
return [
datetime.date,
datetime.datetime,
# datetime.datetimetz,
datetime.time,
# datetime.timedelta,
# datetime.timetz
]
getTypes = staticmethod(getTypes)
class DefaultFieldRepresentation:
__implements__ = ITypeRepresentation
def __init__(self, field):
# This field is described by a schema, or schemas, as found in its
# __implements__ attribute. The fields of this field's schema are
# its properties -- that is, the things we give as arguments when
# constructing this field.
# We're going to get these properties, get source-code
# representations of them, and sort out appropriate imports.
names = {} # used as set of property names, ignoring values
visitImplements(field.__implements__, field,
lambda interface: names.update(getFields(interface)))
# getFields only returns data for Fields in the interface.
# Otherwise, it returns an empty dict.
# At this point, name_property is a dict with keys of the
# property names, and values of property objects.
# XXX I don't know if we're following proper MRO though.
global typeRegistry
self.importList = self._getImportList(field)
arguments = []
# don't represent order of this field within its schema,
# as that will be implicit
if 'order' in names:
del names['order']
# we want to order the field constructor arguments according to the
# order of the fields on the schema describing this field
propertysorter = lambda x, y: cmp(x[1].order, y[1].order)
names_items = names.items()
names_items.sort(propertysorter)
# make a representation of property value and collect necessary imports
# we are not interested in the property field itself, just
# property value
for name, property in names_items:
value = getattr(field, name)
if property.default == value:
continue
representation = typeRegistry.represent(value)
arguments.append((name, representation.text))
for import_spec in representation.importList:
self.importList.append(import_spec)
arguments_text = ', '.join(["%s=%s" % item for item in arguments])
self.text = "%s(%s)" % (type(field).__name__, arguments_text)
def getTypes():
return ()
getTypes = staticmethod(getTypes)
def _getImportList(field):
import Zope.Schema
field_class = type(field)
if getattr(Zope.Schema, field_class.__name__, None) is field_class:
module_name = 'Zope.Schema'
else:
module_name = field_class.__module__
return [(module_name, field_class.__name__)]
_getImportList = staticmethod(_getImportList)
typeRegistry = TypeRepresentationRegistry(DefaultTypeRepresentation)
typeRegistry.register(DatetimeRepresentation)
fieldRegistry = TypeRepresentationRegistry(DefaultFieldRepresentation)