Blender V2.61 - r43446

bl_test.py

Go to the documentation of this file.
00001 # ##### BEGIN GPL LICENSE BLOCK #####
00002 #
00003 #  This program is free software; you can redistribute it and/or
00004 #  modify it under the terms of the GNU General Public License
00005 #  as published by the Free Software Foundation; either version 2
00006 #  of the License, or (at your option) any later version.
00007 #
00008 #  This program is distributed in the hope that it will be useful,
00009 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011 #  GNU General Public License for more details.
00012 #
00013 #  You should have received a copy of the GNU General Public License
00014 #  along with this program; if not, write to the Free Software Foundation,
00015 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00016 #
00017 # ##### END GPL LICENSE BLOCK #####
00018 
00019 # <pep8 compliant>
00020 
00021 import sys
00022 import os
00023 
00024 
00025 # may split this out into a new file
00026 def replace_bpy_app_version():
00027     """ So MD5's are predictable from output which uses blenders versions.
00028     """
00029 
00030     import bpy
00031 
00032     app = bpy.app
00033     app_fake = type(bpy)("bpy.app")
00034 
00035     for attr in dir(app):
00036         if not attr.startswith("_"):
00037             setattr(app_fake, attr, getattr(app, attr))
00038 
00039     app_fake.version = 0, 0, 0
00040     app_fake.version_string = "0.00 (sub 0)"
00041     bpy.app = app_fake
00042 
00043 
00044 def clear_startup_blend():
00045     import bpy
00046 
00047     for scene in bpy.data.scenes:
00048         for obj in scene.objects:
00049             scene.objects.unlink(obj)
00050 
00051 
00052 def blend_to_md5():
00053     import bpy
00054     scene = bpy.context.scene
00055     ROUND = 4
00056 
00057     def matrix2str(matrix):
00058         return "".join([str(round(axis, ROUND)) for vector in matrix for axis in vector]).encode('ASCII')
00059 
00060     def coords2str(seq, attr):
00061         return "".join([str(round(axis, ROUND)) for vertex in seq for axis in getattr(vertex, attr)]).encode('ASCII')
00062 
00063     import hashlib
00064 
00065     md5 = hashlib.new("md5")
00066     md5_update = md5.update
00067 
00068     for obj in scene.objects:
00069         md5_update(matrix2str(obj.matrix_world))
00070         data = obj.data
00071 
00072         if type(data) == bpy.types.Mesh:
00073             md5_update(coords2str(data.vertices, "co"))
00074         elif type(data) == bpy.types.Curve:
00075             for spline in data.splines:
00076                 md5_update(coords2str(spline.bezier_points, "co"))
00077                 md5_update(coords2str(spline.points, "co"))
00078 
00079     return md5.hexdigest()
00080 
00081 
00082 def main():
00083     argv = sys.argv
00084     print("  args:", " ".join(argv))
00085     argv = argv[argv.index("--") + 1:]
00086 
00087     def arg_extract(arg, optional=True, array=False):
00088         arg += "="
00089         if array:
00090             value = []
00091         else:
00092             value = None
00093 
00094         i = 0
00095         while i < len(argv):
00096             if argv[i].startswith(arg):
00097                 item = argv[i][len(arg):]
00098                 del argv[i]
00099                 i -= 1
00100 
00101                 if array:
00102                     value.append(item)
00103                 else:
00104                     value = item
00105                     break
00106 
00107             i += 1
00108 
00109         if (not value) and (not optional):
00110             print("  '%s' not set" % arg)
00111             sys.exit(1)
00112 
00113         return value
00114 
00115     run = arg_extract("--run", optional=False)
00116     md5 = arg_extract("--md5", optional=False)
00117     md5_method = arg_extract("--md5_method", optional=False)  # 'SCENE' / 'FILE'
00118 
00119     # only when md5_method is 'FILE'
00120     md5_source = arg_extract("--md5_source", optional=True, array=True)
00121 
00122     # save blend file, for testing
00123     write_blend = arg_extract("--write-blend", optional=True)
00124 
00125     # ensure files are written anew
00126     for f in md5_source:
00127         if os.path.exists(f):
00128             os.remove(f)
00129 
00130     import bpy
00131 
00132     replace_bpy_app_version()
00133     if not bpy.data.filepath:
00134         clear_startup_blend()
00135 
00136     print("  Running: '%s'" % run)
00137     print("  MD5: '%s'!" % md5)
00138 
00139     try:
00140         result = eval(run)
00141     except:
00142         import traceback
00143         traceback.print_exc()
00144         sys.exit(1)
00145 
00146     if write_blend is not None:
00147         print("  Writing Blend: %s" % write_blend)
00148         bpy.ops.wm.save_mainfile(filepath=write_blend, check_existing=False)
00149 
00150     print("  Result: '%s'" % str(result))
00151     if not result:
00152         print("  Running: %s -> False" % run)
00153         sys.exit(1)
00154 
00155     if md5_method == 'SCENE':
00156         md5_new = blend_to_md5()
00157     elif md5_method == 'FILE':
00158         if not md5_source:
00159             print("  Missing --md5_source argument")
00160             sys.exit(1)
00161 
00162         for f in md5_source:
00163             if not os.path.exists(f):
00164                 print("  Missing --md5_source=%r argument does not point to a file")
00165                 sys.exit(1)
00166 
00167         import hashlib
00168 
00169         md5_instance = hashlib.new("md5")
00170         md5_update = md5_instance.update
00171 
00172         for f in md5_source:
00173             filehandle = open(f, "rb")
00174             md5_update(filehandle.read())
00175             filehandle.close()
00176 
00177         md5_new = md5_instance.hexdigest()
00178 
00179     else:
00180         print("  Invalid --md5_method=%s argument is not a valid source")
00181         sys.exit(1)
00182 
00183     if md5 != md5_new:
00184         print("  Running: %s\n    MD5 Recieved: %s\n    MD5 Expected: %s" % (run, md5_new, md5))
00185         sys.exit(1)
00186 
00187     print("  Success: %s" % run)
00188 
00189 
00190 if __name__ == "__main__":
00191     # So a python error exits(1)
00192     try:
00193         main()
00194     except:
00195         import traceback
00196         traceback.print_exc()
00197         sys.exit(1)