Automatic Code Generation

Rasmus Fogh rhf22 at mole.bio.cam.ac.uk
Tue Oct 7 06:52:23 EDT 2003


On Mon, 6 Oct 2003, Ian Bicking wrote:

> On Monday, October 6, 2003, at 07:24 AM, Rasmus Fogh wrote:
> >> I would disagree that code generation is a good solution to these, or
> >> nearly any case.  Specifically in Python, code generation isn't
> >> necessary because you can build objects and classes dynamically.  So
> >> if
> >> you have a non-Python representation of the object or class, you can
> >> write code to dynamically create that class, instead of writing code
> >> that writes code.
> >>
> >
> > Building the code dynamically would certainly be an alternative, so
> > code
> > generation is not necessary (i.e. unavoidable).
>
> Not to belabor the point, but I wouldn't propose just dynamically
> creating the code.  If you are generating *code*, there's nothing wrong
> with writing it to disk and turning it into a compilation step (it can
> be nice if you can hide that compilation, but it's not that big a deal).
>
> I most cases -- especially using new-style classes and descriptors --
> you do not need to generate code.  For instance, SQLObject (an
> object-relational mapper that I wrote) generates only a small amount of
> code (a very small number of eval'ed lambda statements).  Several
> similar packages have you write a separate model, and then compile that
> module into code, which sounds like the same kind of mechanism you are
> using for your models.
>
> I do some of this with a metaclass, but that is largely to make the
> Python classes look prettier.  The real work happens in class methods
> that add and remove attributes from the class itself.  In fact, most of
> it doesn't even require new-style classes, but simply takes advantage
> of the ability to modify classes after they have been created.
>

[snip - comments about performance]

Undoubtedly a more elegant way of doing things. Always the simplest or the
most appropriate? not so sure. To illustrate the point look at the
property below, as it comes out of our automatic code generation. Take my
word that all the tests are necessary - we need to let users lose on the
API while keeping the data conforming to our model. We also need to
minimise the number of function calls for speed reasons.

The code below is for:
- an object-to-object link
- two-way
- many-to-many
- with minimum two members at one end and no minimum at the other end
- with no maximum to the number of members at either end
- frozen at one end
- without explicitly entered constraints specific to this link
- between classes in the same package
In other cases any of these bullet points could be changed independently,
which would require different code.

Here goes:

class PeakSplitContribN(AbstractPeakSplitContrib):

  # __init__ and many other functions skipped

  def getResonances(self):

    return tuple(self.__dict__['resonances'])

  def setResonances(self, value):

    current_value = self.__dict__['resonances']

    if (type(value) not in (types.ListType, types.TupleType)):
      raise ApiError(
       'ccp.Nmr.PeakSplitContribN.resonances: value must be list or tuple'
      )

    values = list(value)
    if values == current_value:
      return

    if (len(values) < 2):
      raise ApiError(
       'ccp.Nmr.PeakSplitContribN.resonances: value violates locard'
      )

    for value in values:
      if (not isinstance(value, Resonance)):
        raise ApiError(
         'ccp.Nmr.PeakSplitContribN.resonances: each element must be in class ccp.Nmr.Resonance'
        )

    for v in current_value:
      if (v not in values):
        raise ApiError(
         'cannot set because link ccp.Nmr.PeakSplitContribN.resonances cannot be removed'
        )

    if not self.__dict__.get('override'):
      storageDict = self.__dict__['storage'].__dict__
      if not storageDict['isReading']:
        raise ApiError(
         'ccp.Nmr.PeakSplitContribN instance cannot set unchangeable resonances'
        )

    for v in current_value:
      if (v not in values):
        v.__dict__['peakSplitContribNs'].remove(self)

    for v in values:
      if (v not in current_value):
        v.__dict__['peakSplitContribNs'].append(self)

    self.__dict__['resonances'] = values

    # in-lined doNotifies(name,object)
    if not self.__dict__.get('override'):
      notifies = self.__class__.notifies

      ll1 = notifies['']
      for notify in ll1:
        notify(self)

      ll = notifies.get('setResonances')
      if ll:
        for notify in ll:
          if notify not in ll1:
            notify(self)

  resonances = property(getResonances, setResonances, None,
  r"""Resonances assigned to PeakSplitContribN
  """)







More information about the Python-list mailing list