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

A table driven routine generates a unit conversion module and a doctest file. The generated module uses the Decimal class to create unit conversion classes which uses class properties to perform the conversion. conversion.py supplies the following classes: Distance Area Volumn Time Velocity Acceleration Mass Force Weight Pressure Frequency Power Temperature

New classes and additional units can easily be created by adding to the table.

Python, 462 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
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
"""
This recipe generates a module convert.py and convertTest.txt which is used
to test conversion.py when it is run.

conversion.py is built from the table defining the unit conversions
  * uses the decimal module as a base class and unit types are class properties.
  * provides exact decimal representation
  * control over precision
  * control over rounding to meet legal or regulatory requirements
  * tracking of significant decimal places
  * results match calculations done by hand
  
conversion.py supplies the following classes:
    Distance
    Area
    Volumn
    Time
    Velocity
    Acceleration
    Mass
    Force
    Weight
    Pressure
    Frequency
    Power
    Temperature
    
"""

from decimal import *

header = """
  conversion.py
  
  Unit Conversion
  Dave Bailey
  4/10/2011
 
  The conversion.py module uses Decimal from the decimal module as the base class
  decimal is based on the General Decimal Arithmetic Specification
  IEEE standard 854-1987
  conversion provides:
     exact decimal representation
     control over precision,
     control over rounding to meet legal or regulatory requirements,
     tracking of significant decimal places
     results match calculations done by hand. 

"""
examples = """
-- Examples:
>>> d = Distance(0.0)
>>> d.mi = 1.0
>>> print 'ft -> mile %.3f, %f, %s, %r' % (d.ft,d.ft,d.ft,d.ft)
ft -> mile 5280.000, 5280.000000, 5280.000000000000000000000000, Decimal('5280.000000000000000000000000')
>>> getcontext().prec = 28
>>> d = Distance(0.0)
>>> d.mi = 1.0
>>> print 'ft -> mile %.3f, %f, %s, %r' % (d.ft,d.ft,d.ft,d.ft)
ft -> mile 5280.000, 5280.000000, 5280.000000000000000000000000, Decimal('5280.000000000000000000000000')

>>> getcontext().prec = 52
>>> d = Distance(0.0)
>>> d.mi = 1.0
>>> print 'ft -> mile %.3f, %f, %s, %r' % (d.ft,d.ft,d.ft,d.ft)
ft -> mile 5280.000, 5280.000000, 5279.999999999999999999999999588007935999999954845670, Decimal('5279.999999999999999999999999588007935999999954845670')

>>> getcontext().prec = 28
>>> with localcontext() as ctx:
...     getcontext().prec = 52
...     d = Distance(0.0)
...     d.mi = 1.0
...     print 'ft -> mile %.3f, %f, %s, %r' % (d.ft,d.ft,d.ft,d.ft)
ft -> mile 5280.000, 5280.000000, 5279.999999999999999999999999588007935999999954845670, Decimal('5279.999999999999999999999999588007935999999954845670')

>>> getcontext().prec
28
>>> d.ft
Decimal('5280.000000000000000000000000')

>>> d0 = Distance('.10')
>>> d = Distance(d0+d0+d0-Decimal('.30'))
>>> '%r' % d.m
"Decimal('0.00')"

>>> d = Distance(.10 + .10 + .10 - .30)
>>> '%r' % d.m
"Decimal('5.5511151231257827021181583404541015625E-17')"

>>> d.m = '1.0'
>>> d.ft
Decimal('3.28083989501312300000')
>>> d.inch
Decimal('39.370078740157476000000')
>>> d.m = 1.0
>>> d.ft
Decimal('3.2808398950131230000')
>>> d.inch
Decimal('39.37007874015747600000')
>>> print d
1 meters (m)
0.000621371 miles (mi)
1.09361 yard (yd)
3.28084 feet (ft)
39.3701 inch (inch)
0.001 kilometers (km)
100 centimeters (cm)
1000 millimeters (mm)
1e+09 nanometer (nm)
>>> d
Decimal('1') meters (m)
Decimal('0.0006213711922373339015151515152') miles (mi)
Decimal('1.093613298337707666666666667') yard (yd)
Decimal('3.2808398950131230000') feet (ft)
Decimal('39.37007874015747600000') inch (inch)
Decimal('0.0010') kilometers (km)
Decimal('1.0E+2') centimeters (cm)
Decimal('1.0E+3') millimeters (mm)
Decimal('1.0E+9') nanometer (nm)

# distance = vt+.5at**2
>>> v = Velocity(49.0332501432)
>>> a = Acceleration(-9.80665002864) # gravity
>>> t = Time(0.0)
>>> print 'initial velocity = %f mps = %f fps' % (v.mps,v.fps)
initial velocity = 49.033250 mps = 160.870243 fps
>>> for sec in range(20):
...     t.sec = sec
...     d = v*t + Decimal(.5)*a*t**2
...     height = Distance(d)
...     if height < 0: break
...     print 't',t.sec,'height',height.m,'m',height.ft,'ft'
t 0 height 0E-47 m 0E-66 ft
t 1 height 44.12992512888000007365008059 m 144.7832189267716379167004149 ft
t 2 height 78.45320022912000013093347661 m 257.3923892031495785185785154 ft
t 3 height 102.9698253007200001718501880 m 337.8275108291338218056343013 ft
t 4 height 117.6798003436800001964002149 m 386.0885838047243677778677731 ft
t 5 height 122.5831253580000002045835572 m 402.1756081299212164352789303 ft
t 6 height 117.6798003436800001964002149 m 386.0885838047243677778677731 ft
t 7 height 102.9698253007200001718501881 m 337.8275108291338218056343016 ft
t 8 height 78.4532002291200001309334767 m 257.3923892031495785185785157 ft
t 9 height 44.1299251288800000736500806 m 144.7832189267716379167004149 ft
t 10 height 0E-25 m 0E-44 ft

from decimal import *

"""

constants = """
from decimal import *

GRAVITY = Decimal('9.80665002864') # m/s2
FT_IN_MI = Decimal('5280.0')
FT_IN_M = Decimal('3.2808398950131230000') 
FT_IN_YD = Decimal('3.0')
INCH_IN_FT = Decimal('12.0')

MI_IN_M = FT_IN_M / FT_IN_MI
YD_IN_M = FT_IN_M / FT_IN_YD
INCH_IN_M = FT_IN_M * INCH_IN_FT
KM_IN_M = Decimal('1.0e-3')
CM_IN_M = Decimal('1.0e2')
MM_IN_M = Decimal('1.0e3')
NM_IN_M = Decimal('1.0e9')

SEC_IN_MIN = Decimal('60.0')
MIN_IN_HR = Decimal('60.0')
DAY_IN_WK = Decimal('7.0')
HR_IN_DAY = Decimal('24.0')
DAY_IN_YR = Decimal('365.24218967')

HR_IN_SEC = Decimal('1.0')/(SEC_IN_MIN * MIN_IN_HR)

G_IN_KG = Decimal('1.0e3')
LB_IN_NEWTON = Decimal('.224808942911188')
OZ_IN_G = Decimal('0.0352739619000')
OZ_IN_LB = Decimal('16.0')
W_IN_HP = Decimal('745.699872')
"""


tables = [
    [
    ["Distance","meters"],
    ["meters","m","Decimal(self)","self._update(Decimal(value))"],
    ["miles","mi","Decimal(self) * MI_IN_M","self._update(Decimal(value) * Decimal('1.0')/MI_IN_M)"],
    ["yard","yd","Decimal(self) * YD_IN_M","self._update(Decimal(value) * Decimal('1.0')/YD_IN_M)"],
    ["feet","ft","Decimal(self) * FT_IN_M","self._update(Decimal(value) * Decimal('1.0')/FT_IN_M)"],
    ["inch","inch","Decimal(self) * INCH_IN_M","self._update(Decimal(value) * Decimal('1.0')/INCH_IN_M)"],
    ["kilometers","km","Decimal(self) * KM_IN_M","self._update(Decimal(value) * Decimal('1.0')/KM_IN_M)"],
    ["centimeters","cm","Decimal(self) * CM_IN_M","self._update(Decimal(value) * Decimal('1.0')/CM_IN_M)"],
    ["millimeters","mm","Decimal(self) * MM_IN_M","self._update(Decimal(value) * Decimal('1.0')/MM_IN_M)"],
    ["nanometer","nm","Decimal(self) * NM_IN_M","self._update(Decimal(value) * Decimal('1.0')/NM_IN_M)"],
    ],
    [
    ["Area","sq_meters"],
    ["sq_meters","m2","Decimal(self)","self._update(Decimal(value))"],
    ["sq_miles","mi2","Decimal(self) * (MI_IN_M * MI_IN_M)","self._update(Decimal(value) * Decimal('1.0')/(MI_IN_M * MI_IN_M))"],
    ["sq_yard","yd2","Decimal(self) * (YD_IN_M * YD_IN_M)","self._update(Decimal(value) * Decimal('1.0')/(YD_IN_M * YD_IN_M))"],
    ["sq_feet","ft2","Decimal(self) * (FT_IN_M * FT_IN_M)","self._update(Decimal(value) * Decimal('1.0')/(FT_IN_M * FT_IN_M))"],
    ["sq_inch","inch2","Decimal(self) * (INCH_IN_M * INCH_IN_M)","self._update(Decimal(value) * Decimal('1.0')/(INCH_IN_M * INCH_IN_M))"],
    ["sq_kilometers","km2","Decimal(self) * (KM_IN_M * KM_IN_M)","self._update(Decimal(value) * Decimal('1.0')/(KM_IN_M * KM_IN_M))"],
    ["sq_centimeters","cm2","Decimal(self) * (CM_IN_M * CM_IN_M)","self._update(Decimal(value) * Decimal('1.0')/(CM_IN_M * CM_IN_M))"],
    ["sq_millimeters","mm2","Decimal(self) * (MM_IN_M * MM_IN_M)","self._update(Decimal(value) * Decimal('1.0')/(MM_IN_M * MM_IN_M))"],
    ],
    [
    ["Volumn","cubic_meters"],
    ["cubic_meters","m3","Decimal(self)","self._update(Decimal(value))"],
    ["cubic_miles","mi3","Decimal(self) * (MI_IN_M * MI_IN_M * MI_IN_M)","self._update(Decimal(value) * Decimal('1.0')/(MI_IN_M * MI_IN_M * MI_IN_M))"],
    ["cubic_yard","yd3","Decimal(self) * (YD_IN_M * YD_IN_M * YD_IN_M)","self._update(Decimal(value) * Decimal('1.0')/(YD_IN_M * YD_IN_M * YD_IN_M))"],
    ["cubic_feet","ft3","Decimal(self) * (FT_IN_M * FT_IN_M * FT_IN_M)","self._update(Decimal(value) * Decimal('1.0')/(FT_IN_M * FT_IN_M * FT_IN_M))"],
    ["cubic_inch","inch3","Decimal(self) * (INCH_IN_M * INCH_IN_M * INCH_IN_M)","self._update(Decimal(value) * Decimal('1.0')/(INCH_IN_M * INCH_IN_M * INCH_IN_M))"],
    ["cubic_kilometers","km3","Decimal(self) * (KM_IN_M * KM_IN_M * KM_IN_M)","self._update(Decimal(value) * Decimal('1.0')/(KM_IN_M * KM_IN_M * KM_IN_M))"],
    ["cubic_centimeters","cm3","Decimal(self) * (CM_IN_M * CM_IN_M * CM_IN_M)","self._update(Decimal(value) * Decimal('1.0')/(CM_IN_M * CM_IN_M * CM_IN_M))"],
    ["cubic_millimeters","mm3","Decimal(self) * (MM_IN_M * MM_IN_M * MM_IN_M)","self._update(Decimal(value) * Decimal('1.0')/(MM_IN_M * MM_IN_M * MM_IN_M))"],
    ],
    [
    ["Time","sec"],
    ["sec","sec","Decimal(self)","self._update(Decimal(value))"],
    ["min","min","Decimal(self) * Decimal('1.0')/SEC_IN_MIN","self._update(Decimal(value) * SEC_IN_MIN)"],
    ["hour","hr","Decimal(self) * Decimal('1.0')/(SEC_IN_MIN*MIN_IN_HR)","self._update(Decimal(value) * (SEC_IN_MIN*MIN_IN_HR))"],
    ["day","day","Decimal(self) * Decimal('1.0')/(HR_IN_DAY*SEC_IN_MIN*MIN_IN_HR)","self._update(Decimal(value) * (HR_IN_DAY*SEC_IN_MIN*MIN_IN_HR))"],
    ["week","wk","Decimal(self) * Decimal('1.0')/(DAY_IN_WK*HR_IN_DAY*SEC_IN_MIN*MIN_IN_HR)","self._update(Decimal(value) * (DAY_IN_WK*HR_IN_DAY*SEC_IN_MIN*MIN_IN_HR))"],
    ["year","yr","Decimal(self) * Decimal('1.0')/(DAY_IN_YR*HR_IN_DAY*SEC_IN_MIN*MIN_IN_HR)","self._update(Decimal(value) * (DAY_IN_YR*HR_IN_DAY*SEC_IN_MIN*MIN_IN_HR))"],
    ],
    [
    ["Velocity","meters_per_sec"],
    ["meters_per_sec","mps","Decimal(self)","self._update(Decimal(value))"],
    ["miles_per_sec","mips","Decimal(self) * MI_IN_M","self._update(Decimal(value) * Decimal('1.0')/MI_IN_M)"],
    ["miles_per_hr","mph","Decimal(self) * (MI_IN_M * SEC_IN_MIN * MIN_IN_HR)","self._update(Decimal(value) * Decimal('1.0')/(MI_IN_M * SEC_IN_MIN * MIN_IN_HR))"],
    ["ft_per_sec","fps","Decimal(self) * FT_IN_M","self._update(Decimal(value) * Decimal('1.0')/FT_IN_M)"],
    ["inch_per_sec","inchps","Decimal(self) * INCH_IN_M","self._update(Decimal(value) * Decimal('1.0')/INCH_IN_M)"],
    ["km_per_hour","kmph","Decimal(self) * (KM_IN_M * SEC_IN_MIN * MIN_IN_HR)","self._update(Decimal(value) * Decimal('1.0')/(KM_IN_M * SEC_IN_MIN * MIN_IN_HR))"],
    ["km_per_sec","kmps","Decimal(self) * KM_IN_M","self._update(Decimal(value) * Decimal('1.0')/KM_IN_M)"],
    ],
    [
    ["Acceleration","meters_per_sq_sec"],
    ["meters_per_sq_sec","mps2","Decimal(self)","self._update(Decimal(value))"],
    ["miles_per_sq_sec","mips2","Decimal(self) * MI_IN_M","self._update(Decimal(value) * Decimal('1.0')/MI_IN_M)"],
    ["miles_per_hr_per_sec","mphps","Decimal(self) * (MI_IN_M * SEC_IN_MIN * MIN_IN_HR)","self._update(Decimal(value) * Decimal('1.0')/(MI_IN_M * SEC_IN_MIN * MIN_IN_HR))"],
    ["ft_per_sq_sec","fps2","Decimal(self) * FT_IN_M","self._update(Decimal(value) * Decimal('1.0')/FT_IN_M)"],
    ["inch_per_sq_sec","ips2","Decimal(self) * INCH_IN_M","self._update(Decimal(value) * Decimal('1.0')/INCH_IN_M)"],
    ["km_per_hour_per_sec","kmphps","Decimal(self) * (KM_IN_M * SEC_IN_MIN * MIN_IN_HR)","self._update(Decimal(value) * Decimal('1.0')/(KM_IN_M * SEC_IN_MIN * MIN_IN_HR))"],
    ["km_per_sq_sec","kmps2","Decimal(self) * KM_IN_M","self._update(Decimal(value) * Decimal('1.0')/KM_IN_M)"],
    ],
    [
    ["Mass","kilogram"],
    ["kilogram","kg","Decimal(self)","self._update(Decimal(value))"],
    ["gram","g","Decimal(self) * Decimal('1000.0')","self._update(Decimal(value) / Decimal('1000.0'))"],
    ["ounce","oz","Decimal(self) * OZ_IN_G * Decimal('1000.0')","self._update(Decimal(value) / OZ_IN_G / Decimal('1000.0'))"],
    ["pounds","lbm","Decimal(self) * (OZ_IN_G / OZ_IN_LB) * Decimal('1000.0')","self._update(Decimal(value)* OZ_IN_LB / OZ_IN_G / Decimal('1000.0') )"],
    ],
    [
    ["Force","newton"], # m*kg*s**2
    ["newton","N","Decimal(self)","self._update(Decimal(value))"],
    ["kilogram-force","kgf","Decimal(self) / GRAVITY","self._update(Decimal(value) * GRAVITY)"],
    ["dyne","dyn","Decimal(self) * Decimal('100000.0')","self._update(Decimal(value) / Decimal('100000.0'))"],
    ["pound-force","lbf","Decimal(self) * (G_IN_KG * OZ_IN_G) / (OZ_IN_LB*GRAVITY)","self._update(Decimal(value) * (OZ_IN_LB * GRAVITY) / (G_IN_KG * OZ_IN_G))"],
    ], 
    [
    ["Weight","kilogram"], # m*kg*s**2
    ["kilogram","kg","Decimal(self)","self._update(Decimal(value))"],
    ["gram","g","Decimal(self) * G_IN_KG ","self._update(Decimal(value) / G_IN_KG)"],
    ["ounce","oz","Decimal(self) * G_IN_KG * OZ_IN_G ","self._update(Decimal(value) / (G_IN_KG * OZ_IN_G))"],
    ["pounds","lbm","Decimal(self) * (G_IN_KG * OZ_IN_G) / (OZ_IN_LB)","self._update(Decimal(value) * (OZ_IN_LB ) / (G_IN_KG * OZ_IN_G))"],
    ], 
    [
    ["Pressure","pascal "],
    ["pascal","Pa","Decimal(self)","self._update(Decimal(value))"],
    ["newton_per_sq_m","Nm2","Decimal(self)","self._update(Decimal(value))"],
    ["kilogram_per_sq_m","kgfpm2","Decimal(self) * Decimal('1.0')/GRAVITY","self._update(Decimal(value) * GRAVITY)"],
    ["pound_per_sq_inch","psi","Decimal(self) * (LB_IN_NEWTON/(INCH_IN_M * INCH_IN_M))","self._update(Decimal(value) * (INCH_IN_M * INCH_IN_M) / LB_IN_NEWTON)"],
    ["pound_per_sq_ft","psf","Decimal(self) * LB_IN_NEWTON/(FT_IN_M * FT_IN_M)","self._update(Decimal(value) * (FT_IN_M * FT_IN_M) / LB_IN_NEWTON)"],
    ],
    [
    ["Frequency","Frequency"],
    ["hertz","Hz","Decimal(self)","self._update(Decimal(value))"],
    ["KHz","KHz","Decimal(self) * Decimal('1.0')/Decimal(1.0e3)","self._update(Decimal(value) * Decimal('1.0e3'))"],
    ["MHz","MHz","Decimal(self) * Decimal('1.0')/Decimal(1.0e6)","self._update(Decimal(value) * Decimal('1.0e6'))"],
    ["GHz","GHz","Decimal(self) * Decimal('1.0')/Decimal(1.0e9)","self._update(Decimal(value) * Decimal('1.0e9'))"],
    ],
    [
    ["Power","Power"],
    ["watts","W","Decimal(self)","self._update(Decimal(value))"],
    ["kilowatt","KW","Decimal(self) * Decimal('1.0')/Decimal('1.0e3')","self._update(Decimal(value) * Decimal('1.0e3'))"],
    ["megawatt","MW","Decimal(self) * Decimal('1.0')/Decimal('1.0e6')","self._update(Decimal(value) * Decimal('1.0e6'))"],
    ["Horsepower","hp","Decimal(self) * Decimal('1.0')/W_IN_HP","self._update(Decimal(value) * W_IN_HP)"],
    ["joulepersec","jps","Decimal(self)","self._update(Decimal(value))"],
    ],
    [
    ["Temperature","degreeK"],
    ["Kelvin","K","Decimal(self)","self._update(Decimal(value))"],
    ["Fahrenheit","F","((Decimal(self) - Decimal('273.15')) * Decimal('9.0')/Decimal('5.0')) + Decimal('32.0')","self._update((Decimal(value) - Decimal('32.0')) * (Decimal('5.0')/Decimal('9.0')) + Decimal('273.15'))"],
    ["Celsius","C","Decimal(self) - Decimal('273.15')","self._update(Decimal(value) + Decimal('273.15'))"],
    ],
]

def build_class(table):
    "build a class for each table i.e. Distance,Velocity,etc."
    name, baseunits = table[0]
    s = '\nclass %(name)s(Decimal):\n' % locals()
    s += '    __slots__ = ("_update",) # generate AttributeError on illegal property; example: if d.yds instead of d.ydgenerate AttributeError example: if d.yds not d.yd\n'
    return s

def build_init(table):
    "update method"
    s = """
    def _update(self,dec):
        self._exp  = dec._exp
        self._sign = dec._sign
        self._int  = dec._int
        self._is_special  = dec._is_special
"""
    return s

def build_str_funct(table):
    "str method"
    fmt1 = "    def __str__(self):\n        s = ''\n"
    fmt2 = "        s += '%%g %(units)s (%(abrev)s)\\n' %% self.%(abrev)s\n"
    name, baseunits = table[0]
    s = fmt1 % locals()
    for data in table[1:]:
        if len(data) == 3:
            units, abrev, value = data
        else:
            units, abrev, value, value2 = data
        s += fmt2 % locals()
    s += '        return s[:-1]\n'
    return s

def build_repr_funct(table):
    "repr method"
    fmt1 = "    def __repr__(self):\n        s = ''\n"
    fmt2 = "        s += '%%r %(units)s (%(abrev)s)\\n' %% self.%(abrev)s\n"
    name, baseunits = table[0]
    s = fmt1 % locals()
    for data in table[1:]:
        if len(data) == 3:
            units, abrev, value = data
        else:
            units, abrev, value, value2 = data
        s += fmt2 % locals()
    s += '        return s[:-1]\n'
    return s


def build_methods(table):
    "setter and getter property methods"
    fmt = """    @property
    def %(abrev)s(self):
        return eval("%(value)s")
    @%(abrev)s.setter
    def %(abrev)s(self, value):
        eval("%(value2)s")\n"""

    s = ''
    name, baseunits = table[0]
    for data in table[1:]:
        if len(data) == 3:
            units, abrev, value = data
            value2 = str(Decimal(1.0)/eval(value))
        else:
            units, abrev, value, value2 = data
        s += fmt % locals()
    return s

def build_header(header,tables,examples):
    "build module description"
    s = header
    s += 'Conversions:\n'
    for table in tables:
        s += '    %s\n' % table[0][0]
    s += examples
    s += '\n"""\n'
    return s


def build_doctest_call(modulename, testfilename):
    "create method to call doctest on module and module test file"
    s = """
def test():
    "test method tests examples and testfile"
    print '\\n**** %s test ****\\n'
    import doctest
    import %s

    doctest.testmod(%s, verbose=True, report=True)
    print doctest.master.summarize()

    doctest.testfile('%s', verbose=True, report=True)
    print doctest.master.summarize()
    
if __name__ == '__main__':
    test()
""" % (modulename,modulename,modulename,testfilename)
    return s

def build_module(tables, modulename):
    "build module from data table "
    s = '"""\n'
    s += build_header(header, tables, examples)

    s += constants
    for table in tables:
        s += build_class(table)
        s += build_init(table)
        s += build_str_funct(table)
        s += build_repr_funct(table)
        s += build_methods(table)
    s += build_doctest_call(modulename, testfilename)
    return s

def build_test(table):
    "build a test for all getters and setters for each class"
    name, baseunits = table[0]
    s = '\n%s conversion class\n' % name
    s += '>>> from conversion import %s\n' % (name)
    s += '>>> %s = %s(0.0)\n' % (name.lower()[0],name)
    args = [arg[1] for arg in table[1:]]
    for arg in args:
        s += '>>> %s.%s = 1.0\n' % (name.lower()[0],arg)
        s += '>>> print %s\n' % (name.lower()[0])
        x = eval('%s()' % name)
        exec('x.%s = 1.0' % arg)
        for line in str(x).split('\n'):
            s += '%s\n' % line
        s += '>>> %s\n' % (name.lower()[0])
        x = eval('%s()' % name)
        exec('x.%s = 1.0' % arg)
        for line in repr(x).split('\n'):
            s += '%s\n' % line
    s += '\n'
    return s


def build_doctest(modulename,testfilename):
    "builds test file for testing %s.py based on table data" % (modulename)
    s = 'building %s' % testfilename
    s += '\n## **** %s Test  ****\n' % (modulename)
    s += 'from %s import *' % (modulename)
    s += '"""'
    for table in tables:
        s += build_test(table)
    return s

if __name__ == '__main__':
    filename = 'conversion.py'
    modulename = filename[:-3]
    testfilename = modulename+'Test.txt'
    
    print 'building', filename
    fp = open(filename,'w')
    s = build_module(tables, modulename)
    print >>fp,s
    fp.close()

    from conversion import *
    print 'building', testfilename
    fp = open(testfilename,'w')
    s = build_doctest(modulename,testfilename)
    print >>fp,s
    fp.close()

I was involved in a project that involved formulas using input data in various units. The project also required greater precision. The decimal module was an easy way to create these formulas. It allows properties to convert to a common system so that all the math operations are done in MKS. t = Time() v = Velocity() t.sec = 10.0 v.mph = 20.0 d = Distance(v*t) print d.ft