Hello, I have attempted to sketch how I would imagine the new API. An interface description (that can also be validated) in sort-of-Python is attached. It's not something completely from scratch; I have looked at the present code a bit, and at the docs you have on the wiki. At present the model is very simple and does not include advanced features, but the idea is there and it can be extended easily.
I would like the coders to have a look and say if they like it. As this is a sketch, it did not take much time to put together, so I don't mind at all if we decide to throw this away. I wanted to use Zope interfaces for declaring the API, but decided that it may not be worth it here to add another dependency. I ran into two design problems here. I think that they would hold for any API, not just the one I sketched, so please bear with me :) 1) how to add a new item to a container, let's say, a new module to a language translation set. I see two ways: * use a special factory class (Abstract Factory pattern) that builds the needed objects, then add them (I prefer this) * have each container implement the add() method so that it instantiates an empty item, adds it and returns it. The new empty item can then be updated with the required data. This works a bit like the Prototype pattern. 2) when to save data. Again, several choices: * straight-through: always carry out the operation at once. Grossly inefficient for strings (imagine adding strings to a module one by one), but might work for higher-level containers * completely explicit: serialization happens when you explicitly call a method save(). This is prone to bugs and not very nice design: it may break the abstraction. * transactional: when you modify an object, it marks itself as "dirty". The Pootle main function calls "db.startTransaction()" at the beginning of processing a request and calls "db.endTransaction()" at the end. endTransaction() would collect the "dirty" objects and write them to disk. I like this one best, as it leaves it to the implementation of the API how to efficiently deal with changes. Any thoughts on these? -- Gintautas Miliauskas http://gintasm.blogspot.com
"""Abstract classes (interfaces) the define the Pootle backend API.
These classes only describe the available operations. New backends should
implement operations described here.
You can use the function validateModule to check that a set of backend classes
implements the interfaces described here.
"""
# === Helper objects ===
class Interface(object): pass
# Fields
class String(Interface): pass
class Unicode(Interface): pass
class Integer(Interface): pass
# === API interfaces ===
class IDatabase(Interface):
def keys(self):
"""Get list of available project keys."""
return [Unicode]
def __getitem__(self, projectid):
"""Get project object by project id."""
return IProject
def add(self, projectid):
"""Add a new project."""
return IProject
class IProject(Interface):
"""An object corresponding to a project.
A project may have a number of translations sorted by language.
"""
db = IDatabase
name = Unicode
description = Unicode
def keys(self):
"""Get list of available language codes."""
return [String]
def __getitem__(self, code):
"""Get language object by language code."""
return ILanguage
def add(self, langname):
"""Add a new language."""
return ILanguage
class ILanguageInfo(Interface):
"""General information about a language."""
name = Unicode # language name
code = String # ISO639 language code
specialchars = [Unicode] # list of special chars
nplurals = Integer
pluralequation = String
class ILanguageStats(Interface):
"""Statistics for a language."""
# TODO
class ILanguage(Interface):
"""A set of translations for modules of a given project in some language."""
project = IProject
languageinfo = ILanguageInfo
def keys(self):
"""Return list of module ids."""
return [String]
def __getitem__(self, modulename):
"""Get module by id."""
return IModule
def add(self, modulename):
"""Add a new module."""
return IModule
def stats(self):
return ILanguageStats
class IModuleStats(Interface):
"""Module statistics."""
# TODO
class IModule(Interface):
language = ILanguage
def stats(self):
return IModuleStats
def translations(self):
"""Return a list of translations."""
return [IMessage]
def __getitem__(self, number):
"""Get translation by index."""
return IMessage
# TODO: get range of translations? add? clear? iter? save?
class ISuggestionList(Interface):
"""A list of suggestions for a particular message."""
# TODO
class IMessage(Interface):
"""A single translatable string."""
module = IModule
suggestions = ISuggestionList
key = Unicode
translation = Unicode
# TODO: plurals, etc.
# === Validation helpers ===
class ImplementationError(Exception):
pass
def validateClass(cls, iface):
"""Validate a given class against an interface."""
for attrname, attr in iface.__dict__:
if attrname.startswith('__'):
continue # ignore internal attributes
# Check for existence of the attribute
try:
real_attr = getattr(cls, attrname)
except AttributeError:
raise ImplementationError('%r does not have %r' % (cls, attrname))
if isinstance(attr, type) and issubclass(attr, Interface): # attribute
pass
elif callable(attr): # method
if not callable(real_attr):
raise ImplementationError('%r of %r is not callable'
% (attrname, cls))
else:
raise AssertionError("shouldn't happen")
def validateModule(module, complete=False):
"""Check classes in a module against interfaces.
The classes to be checked should have the atttribute _interface
pointing to the implemented interface.
"""
ifaces = set()
for name, cls in module.__dict__:
if isinstance(cls, type):
iface = getattr(cls, '_interface', None)
if iface is not None:
validateClass(cls, iface)
ifaces.add(iface)
if complete:
pass # TODO: check if all interfaces were implemented at least once?
signature.asc
Description: PGP signature
_______________________________________________ Translate-pootle mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/translate-pootle
