Welcome, guest | Sign In | My Account | Store | Cart

This little script takes a yaml file as a class definition and generates code stubs and unit tests.

Python, 269 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
import os
import sys
import re
import yaml
from new import classobj

"""
Yaml file looks like this -

Class:
    Name: GeneratedClassCode
    Super: Whatever
    DocString: You better comment this Class
    Args: firstArg, secondArg 
    Kwds: this=that, andThat=thisThing
    Methods:
        sync_method:
            Sig:
                Sync: True
                Args: date
                Kwds: keywords
                DocString: This is my doc string
        unsynced_method:
            Sig:
                Sync: False
                Args:
                Kwds:
                DocString: This is my other doc string

And gererates this -

import sys
import os
import yaml
import re
from new import classobj
import inspect
import threading
import unittest
#
#
#
class GeneratedClassCode(Whatever):
  '''
    You better comment this Class
  '''
    def __init__(self, firstArg, secondArg, this=that, andThat=thisThing):
      '''
        Comment this Method
      '''
      Whatever.__init__(self)
      try:
        self.generatedClassCodeLock = threading.RLock()
        self.firstArg = firstArg
        self.secondArg = secondArg
        self.this = that
        self.andThat = thisThing

      except Exception, e:
        raise Exception(
          'Raising Exception "%s" from %s.%s()'%(e, self.__class__.__name__, str(inspect.stack()[0][3]))
        )
    @synchronous(generatedClassCodeLock)
    def sync_method(self, date, keywords):
      '''
        This is my doc string
      '''
      try:
        pass
      except Exception, e:
        raise Exception(
          'Raising Exception "%s" from %s.%s()'%(e, self.__class__.__name__, str(inspect.stack()[0][3]))
        )
    def unsynced_method(self, *args, **kwds):
      '''
        This is my other doc string
      '''
      try:
        pass
      except Exception, e:
        raise Exception(
          'Raising Exception "%s" from %s.%s()'%(e, self.__class__.__name__, str(inspect.stack()[0][3]))
        )

#
#
#
class TestGeneratedClassCode(unittest.TestCase):
    def setUp(self, *args, **kwds):
      '''
        Comment this Method
      '''
      try:
        pass
      except Exception, e:
        raise Exception(
          'Raising Exception "%s" from %s.%s()'%(e, self.__class__.__name__, str(inspect.stack()[0][3]))
        )
    def tearDown(self, *args, **kwds):
      '''
        Comment this Method
      '''
      try:
        pass
      except Exception, e:
        raise Exception(
          'Raising Exception "%s" from %s.%s()'%(e, self.__class__.__name__, str(inspect.stack()[0][3]))
        )
    def test_sync_method(self, *args, **kwds):
      '''
        Comment this Method
      '''
      try:
        pass
      except Exception, e:
        raise Exception(
          'Raising Exception "%s" from %s.%s()'%(e, self.__class__.__name__, str(inspect.stack()[0][3]))
        )
    def test_unsynced_method(self, *args, **kwds):
      '''
        Comment this Method
      '''
      try:
        pass
      except Exception, e:
        raise Exception(
          'Raising Exception "%s" from %s.%s()'%(e, self.__class__.__name__, str(inspect.stack()[0][3]))
        )


"""

#
#
#
class ConfigOptions(object):
  def __init__(self, **kwds):
    self.set_inners(**kwds)
  def set_inners(self, **kwds):
    for k,v in kwds.items():
      if type(v) == dict:
        setattr(self, k, ConfigOptions(**v))
      else:
        setattr(self, k,v)    
#   
def get_options_dict(configFile):
  f = open(configFile)
  d = yaml.load(f)
  f.close()
  objs = {}
  for k,v in d.items():
    oClz = classobj('%sOptions'%k.capitalize(),(ConfigOptions,), {})
    obj = oClz(**d[k])
    objs[oClz.__name__]=obj
  return ConfigOptions(**objs)
#
def code_line(line, tabIn=1):
  tSeq = list(('\t' for t in xrange(0,tabIn)))
  cSeq = ['%s\n'%line]
  lineSeq = tSeq + cSeq
  return ''.join(lineSeq)
#
def docstring(docstring, tabIn=1):
  tSeq = list(('\t' for t in xrange(0,tabIn)))
  sSeq = ["'''\n"]
  docSeq = tSeq + sSeq + tSeq + ['\t%s\n'%docstring] + tSeq + sSeq
  return ''.join(docSeq)
#
def parse_args(args):
  return (args if args != None else '*args')
#
def parse_kwds(keywords):
  return (keywords if keywords != None else '**kwds')
#
def make_lock(line):
  return 'self.%s%sLock = threading.RLock()\n'%(
    line[0].lower(), line[1:len(options.ClassOptions.Name)])
#
def define_method(methodName, arguments, keywords, 
      synchronised=False, docStr='Comment this Method', tryExcept=True):
  mSeq = []
  if synchronised: mSeq.append(
      code_line('@synchronous("%s%sLock")'%(
        options.ClassOptions.Name[0].lower(),
        options.ClassOptions.Name[1:len(options.ClassOptions.Name)]),
        tabIn=2))
  mSeq.append(code_line('def %s(self, %s, %s):'%( 
        methodName,
        (arguments if arguments != None else '*args'),
        (keywords if keywords != None else '**kwds')),
        tabIn=2))
  mSeq.append(docstring(docStr, tabIn=3))
  if tryExcept:
    mSeq.append(try_except())
  return ''.join(mSeq)
#
def try_except(line='pass'):
  return '''\t\t\ttry:\n\t\t\t\t'''+line+'''\n\t\t\texcept Exception, e:\n\t\t\t\traise Exception(\n\t\t\t\t\t'Raising Exception "%s" from %s.%s()'%(e, self.__class__.__name__, str(inspect.stack()[0][3]))\n\t\t\t\t)\n'''
#
def get_kwds(line):
  if line:
    line = line.replace(' ', '')
    g = (k.split('=') for k in line.split(','))
    return dict(g)
  return {}
#
def get_args(line):
  if line:
    return line.replace(' ', '').split(',')
#
def define_members(args, kwds):
  memSeq = []
  if args:
    memSeq = list(('\t\t\t\tself.%s = %s\n'%(a, a) for a in args))
  if kwds:
    for k,v in kwds.items():
      memSeq.append('\t\t\t\tself.%s = %s\n'%(k,v))
  return ''.join(memSeq)
#
def define_class(options):
  classSeq = []
  classSeq.append(code_line('#\n#\n#\nclass %s(%s):'%(
      options.ClassOptions.Name,
      ('object' if options.ClassOptions.Super==None else options.ClassOptions.Super)),
      tabIn=0))
  classSeq.append(docstring(options.ClassOptions.DocString))
  # add __init__
  classSeq.append(define_method(  '__init__',
                  parse_args(options.ClassOptions.Args),
                  parse_kwds(options.ClassOptions.Kwds),
                  tryExcept=False)
                )
  if options.ClassOptions.Super != None:
    classSeq.append(code_line('%s.__init__(self)'%options.ClassOptions.Super, tabIn=3))
  memberLines = define_members(
        get_args(options.ClassOptions.Args), 
        get_kwds(options.ClassOptions.Kwds))
  classSeq.append(
    try_except(line=make_lock(options.ClassOptions.Name)+memberLines)
  )
  # add methods
  for mName, mOptions in options.ClassOptions.Methods.__dict__.items():
    classSeq.append(
      define_method(
        mName, 
        mOptions.Sig.Args, 
        mOptions.Sig.Kwds, 
        mOptions.Sig.Sync, 
        mOptions.Sig.DocString)
    )
  
  return ''.join(classSeq)
#
def define_tests(options):
  testSeq = []
  testSeq.append(code_line('#\n#\n#\nclass Test%s(%s):'%(
          options.ClassOptions.Name,'unittest.TestCase'), tabIn=0))
  testSeq.append(define_method('setUp',None, None))
  testSeq.append(define_method('tearDown',None, None))
  for mName, mOptions in options.ClassOptions.Methods.__dict__.items():
    testSeq.append(define_method('test_%s'%mName, None, None))
  
  return ''.join(testSeq)
#
if __name__ == '__main__':
  options = get_options_dict(sys.argv[1])
  print 'import sys\nimport os\nimport yaml\nimport re\nfrom new import classobj\nimport inspect\nimport threading\nimport unittest'
  print define_class(options)
  print define_tests(options)

When starting a new component or application, I find myself typing out the same thing over and over, using Snippets in my Toolbox, etc.. A lot of setup time is spent organising, fixing little mistakes, going back to add something forgotten and the like.

The goal of this script was to reduce the amount of typing when starting from well defined requirements. I was also interested in creating uniform looking code, which can be helpful when dealing with hundreds of different db, model, or view controllers.

I am also lazy....

So nothing fancy here, its really basic, but banging this out one morning saved me hours of set up and mindless typing. I raise exceptions from each method to help me track down problems right away during development. There are plenty more interesting options that could be added because of that great yaml module...

Also, I've included the @synchronous decorator from http://code.activestate.com/recipes/577105-synchronization-decorator-for-class-methods/

Someday I would like to use a sort of markup for class, method, and unit test templates that could be referenced in the yaml to specify different types of output, instead of writing out the text inline.

5 comments

jonathansamuel 14 years ago  # | flag

Could someone provide an example of how I might use this in an actual "hello world" program? Am I supposed to inherit from this code, or what?

Mateyuzo (author) 14 years ago  # | flag

Its a helper script that saves time when starting from requirements documents, UML class definitions, or from a schema.

Though, one could write a yaml file like -

Class:

Name: HelloWorld
Super:
DocString: This Hello World Class has one method say_hi that will print out a message
Args: 
Kwds:
Methods:
    say_hi:
        Sig:
            Sync: False
            Args: whatToSay
            Kwds:
            DocString: This method prints a message

and save this script as gen.py run it like

:~ matmathews$ python gen.py helloworld.yaml > hello.py

One would then have to edit the say_hi method, replacing 'pass' with 'print whatToSay'

But that sure is a lot of work!

Will Henderson 14 years ago  # | flag

Are you using the decorator described here?:

http://code.activestate.com/recipes/577105-synchronization-decorator-for-class-methods/

I had to add quotes around the lock reference, eg:

@synchronous(generatedClassCodeLock) should be @synchronous('generatedClassCodeLock')

Otherwise worked like a charm, nice recipe!

Mateyuzo (author) 14 years ago  # | flag

Hey thanks for catching that Will!

aykut 10 years, 6 months ago  # | flag

thank you for sharing..