diff options
Diffstat (limited to 'source/blender/python')
-rw-r--r-- | source/blender/python/BPY_menus.c | 1118 | ||||
-rw-r--r-- | source/blender/python/BPY_menus.h | 128 | ||||
-rw-r--r-- | source/blender/python/Makefile | 34 | ||||
-rw-r--r-- | source/blender/python/epy_doc_gen.py | 753 | ||||
-rw-r--r-- | source/blender/python/generic/bpy_internal_import.h | 2 | ||||
-rw-r--r-- | source/blender/python/intern/Makefile | 67 | ||||
-rw-r--r-- | source/blender/python/intern/bpy.c | 2 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_operator_wrap.c | 1 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_rna.h | 1 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_ui.c | 69 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_ui.h | 31 | ||||
-rw-r--r-- | source/blender/python/simple_enum_gen.py | 54 |
12 files changed, 2230 insertions, 30 deletions
diff --git a/source/blender/python/BPY_menus.c b/source/blender/python/BPY_menus.c new file mode 100644 index 00000000000..b67f1e717da --- /dev/null +++ b/source/blender/python/BPY_menus.c @@ -0,0 +1,1118 @@ +/* + * $Id: BPY_menus.c 12932 2007-12-17 20:21:06Z theeth $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * This is a new part of Blender. + * + * Contributor(s): Willian P. Germano, Michael Reimpell + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** +*/ + +/* + *This is the main file responsible for having bpython scripts accessible + * from Blender menus. To know more, please start with its header file. + */ + +#include "BPY_menus.h" + +#include <Python.h> +#ifndef WIN32 + #include <dirent.h> +#else + #include "BLI_winstuff.h" +#endif +#include "BKE_global.h" +#include "BKE_utildefines.h" +#include "BLI_blenlib.h" +#include "MEM_guardedalloc.h" +#include "DNA_userdef_types.h" /* for U.pythondir */ +#include "api2_2x/EXPP_interface.h" /* for bpy_gethome() */ + +#define BPYMENU_DATAFILE "Bpymenus" +#define MAX_DIR_DEPTH 4 /* max depth for traversing scripts dirs */ +#define MAX_DIR_NUMBER 30 /* max number of dirs in scripts dirs trees */ + +static int DEBUG; +static int Dir_Depth; +static int Dirs_Number; + +/* BPyMenuTable holds all registered pymenus, as linked lists for each menu + * where they can appear (see PYMENUHOOKS enum in BPY_menus.h). +*/ +BPyMenu *BPyMenuTable[PYMENU_TOTAL]; + +static int bpymenu_group_atoi( char *str ) +{ + if( !strcmp( str, "Export" ) ) + return PYMENU_EXPORT; + else if( !strcmp( str, "Import" ) ) + return PYMENU_IMPORT; + else if( !strcmp( str, "Help" ) ) + return PYMENU_HELP; + else if( !strcmp( str, "HelpWebsites" ) ) + return PYMENU_HELPWEBSITES; + else if( !strcmp( str, "HelpSystem" ) ) + return PYMENU_HELPSYSTEM; + else if( !strcmp( str, "Render" ) ) + return PYMENU_RENDER; + else if( !strcmp( str, "System" ) ) + return PYMENU_SYSTEM; + else if( !strcmp( str, "Object" ) ) + return PYMENU_OBJECT; + else if( !strcmp( str, "Mesh" ) ) + return PYMENU_MESH; + else if( !strncmp( str, "Theme", 5 ) ) + return PYMENU_THEMES; + else if( !strcmp( str, "Add" ) ) + return PYMENU_ADD; + else if( !strcmp( str, "Wizards" ) ) + return PYMENU_WIZARDS; + else if( !strcmp( str, "Animation" ) ) + return PYMENU_ANIMATION; + else if( !strcmp( str, "Materials" ) ) + return PYMENU_MATERIALS; + else if( !strcmp( str, "UV" ) ) + return PYMENU_UV; + else if( !strcmp( str, "Image" ) ) + return PYMENU_IMAGE; + else if( !strcmp( str, "FaceSelect" ) ) + return PYMENU_FACESELECT; + else if( !strcmp( str, "WeightPaint" ) ) + return PYMENU_WEIGHTPAINT; + else if( !strcmp( str, "VertexPaint" ) ) + return PYMENU_VERTEXPAINT; + else if( !strcmp( str, "UVCalculation" ) ) + return PYMENU_UVCALCULATION; + else if( !strcmp( str, "Armature" ) ) + return PYMENU_ARMATURE; + else if( !strcmp( str, "ScriptTemplate" ) ) + return PYMENU_SCRIPTTEMPLATE; + else if( !strcmp( str, "MeshFaceKey" ) ) + return PYMENU_MESHFACEKEY; + else if( !strcmp( str, "AddMesh" ) ) + return PYMENU_ADDMESH; + /* "Misc" or an inexistent group name: use misc */ + else + return PYMENU_MISC; +} + +char *BPyMenu_group_itoa( short menugroup ) +{ + switch ( menugroup ) { + case PYMENU_EXPORT: + return "Export"; + break; + case PYMENU_IMPORT: + return "Import"; + break; + case PYMENU_ADD: + return "Add"; + break; + case PYMENU_HELP: + return "Help"; + break; + case PYMENU_HELPWEBSITES: + return "HelpWebsites"; + break; + case PYMENU_HELPSYSTEM: + return "HelpSystem"; + break; + case PYMENU_RENDER: + return "Render"; + break; + case PYMENU_SYSTEM: + return "System"; + break; + case PYMENU_OBJECT: + return "Object"; + break; + case PYMENU_MESH: + return "Mesh"; + break; + case PYMENU_THEMES: + return "Themes"; + break; + case PYMENU_WIZARDS: + return "Wizards"; + break; + case PYMENU_ANIMATION: + return "Animation"; + break; + case PYMENU_MATERIALS: + return "Materials"; + break; + case PYMENU_UV: + return "UV"; + break; + case PYMENU_IMAGE: + return "Image"; + break; + case PYMENU_FACESELECT: + return "FaceSelect"; + break; + case PYMENU_WEIGHTPAINT: + return "WeightPaint"; + break; + case PYMENU_VERTEXPAINT: + return "VertexPaint"; + break; + case PYMENU_UVCALCULATION: + return "UVCalculation"; + break; + case PYMENU_ARMATURE: + return "Armature"; + break; + case PYMENU_SCRIPTTEMPLATE: + return "ScriptTemplate"; + break; + case PYMENU_MESHFACEKEY: + return "MeshFaceKey"; + break; + case PYMENU_ADDMESH: + return "AddMesh"; + break; + case PYMENU_MISC: + return "Misc"; + break; + } + return NULL; +} + +/* BPyMenu_CreatePupmenuStr: + * build and return a meaninful string to be used by pupmenu(). The + * string is made of a bpymenu name as title and its submenus as possible + * choices for the user. +*/ +char *BPyMenu_CreatePupmenuStr( BPyMenu * pym, short menugroup ) +{ + BPySubMenu *pysm = pym->submenus; + char str[1024], str2[100]; + int i = 0, rlen; + + if( !pym || !pysm ) + return NULL; + + str[0] = '\0'; + + PyOS_snprintf( str2, sizeof( str2 ), "%s: %s%%t", + BPyMenu_group_itoa( menugroup ), pym->name ); + strcat( str, str2 ); + + while( pysm ) { + PyOS_snprintf( str2, sizeof( str2 ), "|%s%%x%d", pysm->name, + i ); + rlen = sizeof( str ) - strlen( str ); + strncat( str, str2, rlen ); + i++; + pysm = pysm->next; + } + + return BLI_strdup( str ); +} + +static void bpymenu_RemoveAllSubEntries( BPySubMenu * smenu ) +{ + BPySubMenu *tmp; + + while( smenu ) { + tmp = smenu->next; + if( smenu->name ) + MEM_freeN( smenu->name ); + if( smenu->arg ) + MEM_freeN( smenu->arg ); + MEM_freeN( smenu ); + smenu = tmp; + } + return; +} + +void BPyMenu_RemoveAllEntries( void ) +{ + BPyMenu *tmp, *pymenu; + int i; + + for( i = 0; i < PYMENU_TOTAL; i++ ) { + pymenu = BPyMenuTable[i]; + while( pymenu ) { + tmp = pymenu->next; + if( pymenu->name ) + MEM_freeN( pymenu->name ); + if( pymenu->filename ) + MEM_freeN( pymenu->filename ); + if( pymenu->tooltip ) + MEM_freeN( pymenu->tooltip ); + if( pymenu->submenus ) + bpymenu_RemoveAllSubEntries( pymenu-> + submenus ); + MEM_freeN( pymenu ); + pymenu = tmp; + } + BPyMenuTable[i] = NULL; + } + + Dirs_Number = 0; + Dir_Depth = 0; + + return; +} + +static BPyMenu *bpymenu_FindEntry( short group, char *name ) +{ + BPyMenu *pymenu; + + if( ( group < 0 ) || ( group >= PYMENU_TOTAL ) ) + return NULL; + + pymenu = BPyMenuTable[group]; + + while( pymenu ) { + if( !strcmp( pymenu->name, name ) ) + return pymenu; + pymenu = pymenu->next; + } + + return NULL; +} + +/* BPyMenu_GetEntry: + * given a group and a position, return the entry in that position from + * that group. +*/ +BPyMenu *BPyMenu_GetEntry( short group, short pos ) +{ + BPyMenu *pym = NULL; + + if( ( group < 0 ) || ( group >= PYMENU_TOTAL ) ) + return NULL; + + pym = BPyMenuTable[group]; + + while( pos-- ) { + if( pym ) + pym = pym->next; + else + break; + } + + return pym; /* found entry or NULL */ +} + +static void bpymenu_set_tooltip( BPyMenu * pymenu, char *tip ) +{ + if( !pymenu ) + return; + + if( pymenu->tooltip ) + MEM_freeN( pymenu->tooltip ); + pymenu->tooltip = BLI_strdup( tip ); + + return; +} + +/* bpymenu_AddEntry: + * try to find an existing pymenu entry with the given type and name; + * if found, update it with new info, otherwise create a new one and fill it. + */ +static BPyMenu *bpymenu_AddEntry( short group, short version, char *name, + char *fname, int is_userdir, char *tooltip ) +{ + BPyMenu *menu, *next = NULL, **iter; + int nameclash = 0; + + if( ( group < 0 ) || ( group >= PYMENU_TOTAL ) ) + return NULL; + if( !name || !fname ) + return NULL; + + menu = bpymenu_FindEntry( group, name ); /* already exists? */ + + /* if a menu with this name already exists in the same group: + * - if one script is in the default dir and the other in U.pythondir, + * accept and let the new one override the other. + * - otherwise, report the error and return NULL. */ + if( menu ) { + if( menu->dir < is_userdir ) { /* new one is in U.pythondir */ + nameclash = 1; + if( menu->name ) + MEM_freeN( menu->name ); + if( menu->filename ) + MEM_freeN( menu->filename ); + if( menu->tooltip ) + MEM_freeN( menu->tooltip ); + if( menu->submenus ) + bpymenu_RemoveAllSubEntries( menu->submenus ); + next = menu->next; + } else { /* they are in the same dir */ + if (DEBUG) { + fprintf(stderr, "\n\ +Warning: script %s's menu name is already in use.\n\ +Edit the script and change its \n\ +Name: '%s'\n\ +field, please.\n\ +Note: if you really want to have two scripts for the same menu with\n\ +the same name, keep one in the default dir and the other in\n\ +the user defined dir (only the later will be registered).\n", fname, name); + } + return NULL; + } + } else + menu = MEM_mallocN( sizeof( BPyMenu ), "pymenu" ); + + if( !menu ) + return NULL; + + menu->name = BLI_strdup( name ); + menu->version = version; + menu->filename = BLI_strdup( fname ); + menu->tooltip = NULL; + if( tooltip ) + menu->tooltip = BLI_strdup( tooltip ); + menu->dir = is_userdir; + menu->submenus = NULL; + menu->next = next; /* non-NULL if menu already existed */ + + if( nameclash ) + return menu; /* no need to place it, it's already at the list */ + else { /* insert the new entry in its correct position at the table */ + BPyMenu *prev = NULL; + char *s = NULL; + + iter = &BPyMenuTable[group]; + while( *iter ) { + s = ( *iter )->name; + if( s ) + if( strcmp( menu->name, s ) < 0 ) + break; /* sort by names */ + prev = *iter; + iter = &( ( *iter )->next ); + } + + if( *iter ) { /* prepend */ + menu->next = *iter; + if( prev ) + prev->next = menu; + else + BPyMenuTable[group] = menu; /* is first entry */ + } else + *iter = menu; /* append */ + } + + return menu; +} + +/* bpymenu_AddSubEntry: + * add a submenu to an existing python menu. + */ +static int bpymenu_AddSubEntry( BPyMenu * mentry, char *name, char *arg ) +{ + BPySubMenu *smenu, **iter; + + smenu = MEM_mallocN( sizeof( BPySubMenu ), "pysubmenu" ); + if( !smenu ) + return -1; + + smenu->name = BLI_strdup( name ); + smenu->arg = BLI_strdup( arg ); + smenu->next = NULL; + + if( !smenu->name || !smenu->arg ) + return -1; + + iter = &( mentry->submenus ); + while( *iter ) + iter = &( ( *iter )->next ); + + *iter = smenu; + + return 0; +} + +/* bpymenu_CreateFromFile: + * parse the bpymenus data file where Python menu data is stored; + * based on this data, create and fill the pymenu structs. + */ +static int bpymenu_CreateFromFile( void ) +{ + FILE *fp; + char line[255], w1[255], w2[255], tooltip[255], *tip; + char *homedir = NULL; + int parsing, version, is_userdir; + short group; + BPyMenu *pymenu = NULL; + + /* init global bpymenu table (it is a list of pointers to struct BPyMenus + * for each available cathegory: import, export, etc.) */ + for( group = 0; group < PYMENU_TOTAL; group++ ) + BPyMenuTable[group] = NULL; + + /* let's try to open the file with bpymenu data */ + homedir = bpy_gethome(0); + if (!homedir) { + if( DEBUG ) + fprintf(stderr, + "BPyMenus error: couldn't open config file Bpymenus: no home dir.\n"); + return -1; + } + + BLI_make_file_string( "/", line, homedir, BPYMENU_DATAFILE ); + + fp = fopen( line, "rb" ); + + if( !fp ) { + if( DEBUG ) + fprintf(stderr, "BPyMenus error: couldn't open config file %s.\n", line ); + return -1; + } + + fgets( line, 255, fp ); /* header */ + + /* check if the U.pythondir we saved at the file is different from the + * current one. If so, return to force updating from dirs */ + w1[0] = '\0'; + fscanf( fp, "# User defined scripts dir: %[^\n]\n", w1 ); + if( w1 ) { + char upythondir[FILE_MAXDIR]; + + BLI_strncpy(upythondir, U.pythondir, FILE_MAXDIR); + BLI_convertstringcode(upythondir, G.sce, 0); + if( strcmp( w1, upythondir ) != 0 ) + return -1; + w1[0] = '\0'; + } + + while( fgets( line, 255, fp ) ) { /* parsing file lines */ + + switch ( line[0] ) { /* check first char */ + case '#': /* comment */ + continue; + break; + case '\n': + continue; + break; + default: + parsing = sscanf( line, "%s {\n", w1 ); /* menu group */ + break; + } + + if( parsing == 1 ) { /* got menu group string */ + group = (short)bpymenu_group_atoi( w1 ); + if( group < 0 && DEBUG ) { /* invalid type */ + fprintf(stderr, + "BPyMenus error parsing config file: wrong group: %s,\n\ +will use 'Misc'.\n", w1 ); + } + } else + continue; + + for(;;) { + tip = NULL; /* optional tooltip */ + fgets( line, 255, fp ); + if( line[0] == '}' ) + break; + else if( line[0] == '\n' ) + continue; + else if( line[0] == '\'' ) { /* menu entry */ + parsing = + sscanf( line, + "'%[^']' %d %s %d '%[^']'\n", + w1, &version, w2, &is_userdir, + tooltip ); + + if( parsing <= 0 ) { /* invalid line, get rid of it */ + fgets( line, 255, fp ); + } else if( parsing == 5 ) + tip = tooltip; /* has tooltip */ + + pymenu = bpymenu_AddEntry( group, + ( short ) version, + w1, w2, is_userdir, + tip ); + if( !pymenu ) { + puts( "BPyMenus error: couldn't create bpymenu entry.\n" ); + fclose( fp ); + return -1; + } + } else if( line[0] == '|' && line[1] == '_' ) { /* menu sub-entry */ + if( !pymenu ) + continue; /* no menu yet, skip this line */ + sscanf( line, "|_%[^:]: %s\n", w1, w2 ); + bpymenu_AddSubEntry( pymenu, w1, w2 ); + } + } + } + + fclose( fp ); + return 0; +} + +/* bpymenu_WriteDataFile: + * writes the registered scripts info to the user's home dir, for faster + * access when the scripts dir hasn't changed. +*/ +static void bpymenu_WriteDataFile( void ) +{ + BPyMenu *pymenu; + BPySubMenu *smenu; + FILE *fp; + char fname[FILE_MAXDIR], *homedir; + int i; + + homedir = bpy_gethome(0); + + if (!homedir) { + if( DEBUG ) + fprintf(stderr, + "BPyMenus error: couldn't write Bpymenus file: no home dir.\n\n"); + return; + } + + BLI_make_file_string( "/", fname, homedir, BPYMENU_DATAFILE ); + + fp = fopen( fname, "w" ); + if( !fp ) { + if( DEBUG ) + fprintf(stderr, "BPyMenus error: couldn't write %s file.\n\n", + fname ); + return; + } + + fprintf( fp, + "# Blender: registered menu entries for bpython scripts\n" ); + + if (U.pythondir[0] != '\0' && + strcmp(U.pythondir, "/") != 0 && strcmp(U.pythondir, "//") != 0) + { + char upythondir[FILE_MAXDIR]; + + BLI_strncpy(upythondir, U.pythondir, FILE_MAXDIR); + BLI_convertstringcode(upythondir, G.sce, 0); + fprintf( fp, "# User defined scripts dir: %s\n", upythondir ); + } + + for( i = 0; i < PYMENU_TOTAL; i++ ) { + pymenu = BPyMenuTable[i]; + if( !pymenu ) + continue; + fprintf( fp, "\n%s {\n", BPyMenu_group_itoa( (short)i ) ); + while( pymenu ) { + fprintf( fp, "'%s' %d %s %d", pymenu->name, + pymenu->version, pymenu->filename, + pymenu->dir ); + if( pymenu->tooltip ) + fprintf( fp, " '%s'\n", pymenu->tooltip ); + else + fprintf( fp, "\n" ); + smenu = pymenu->submenus; + while( smenu ) { + fprintf( fp, "|_%s: %s\n", smenu->name, + smenu->arg ); + smenu = smenu->next; + } + pymenu = pymenu->next; + } + fprintf( fp, "}\n" ); + } + + fclose( fp ); + return; +} + +/* BPyMenu_PrintAllEntries: + * useful for debugging. + */ +void BPyMenu_PrintAllEntries( void ) +{ + BPyMenu *pymenu; + BPySubMenu *smenu; + int i; + + printf( "# Blender: registered menu entries for bpython scripts\n" ); + + for( i = 0; i < PYMENU_TOTAL; i++ ) { + pymenu = BPyMenuTable[i]; + printf( "\n%s {\n", BPyMenu_group_itoa( (short)i ) ); + while( pymenu ) { + printf( "'%s' %d %s %d", pymenu->name, pymenu->version, + pymenu->filename, pymenu->dir ); + if( pymenu->tooltip ) + printf( " '%s'\n", pymenu->tooltip ); + else + printf( "\n" ); + smenu = pymenu->submenus; + while( smenu ) { + printf( "|_%s: %s\n", smenu->name, + smenu->arg ); + smenu = smenu->next; + } + pymenu = pymenu->next; + } + printf( "}\n" ); + } +} + +/* bpymenu_ParseFile: + * recursively scans folders looking for scripts to register. + * + * This function scans the scripts directory looking for .py files with the + * right header and menu info, using that to fill the bpymenu structs. + * is_userdir defines if the script is in the default scripts dir or the + * user defined one (U.pythondir: is_userdir == 1). + * Speed is important. + * + * The first line of the script must be '#!BPY'. + * The header registration lines must appear between the first pair of + * '\"\"\"' and follow this order (the single-quotes are part of + * the format): + * + * # \"\"\"<br> + * # Name: 'script name for the menu' + * # Blender: <code>short int</code> (minimal Blender version) + * # Group: 'group name' (defines menu) + * # Submenu: 'submenu name' related_1word_arg + * # Tooltip: 'tooltip for the menu' + * # \"\"\" + * + * Notes: + * + * - Commenting out header lines with "#" is optional, but recommended. + * - There may be more than one submenu line, or none: + * submenus and the tooltip are optional; + * - The Blender version is the same number reported by + * Blender.Get('version') in BPython or G.version in C; + * - Line length must be less than 99. + */ +static int bpymenu_ParseFile(FILE *file, char *fname, int is_userdir) +{ + char line[100]; + char head[100]; + char middle[100]; + char tail[100]; + int matches; + int parser_state; + + char script_name[100]; + int script_version = 1; + int script_group; + + BPyMenu *scriptMenu = NULL; + + if (file != NULL) { + parser_state = 1; /* state of parser, 0 to terminate */ + + while ((parser_state != 0) && (fgets(line, 100, file) != NULL)) { + + switch (parser_state) { + + case 1: /* !BPY */ + if (strncmp(line, "#!BPY", 5) == 0) { + parser_state++; + } else { + parser_state = 0; + } + break; + + case 2: /* \"\"\" */ + if ((strstr(line, "\"\"\""))) { + parser_state++; + } + break; + + case 3: /* Name: 'script name for the menu' */ + matches = sscanf(line, "%[^']'%[^']'%c", head, script_name, tail); + if ((matches == 3) && (strstr(head, "Name:") != NULL)) { + parser_state++; + } else { + if (DEBUG) + fprintf(stderr, "BPyMenus error: Wrong 'Name' line: %s\n", fname); + parser_state = 0; + } + break; + + case 4: /* Blender: <short int> */ + matches = sscanf(line, "%[^1234567890]%i%c", head, &script_version, + tail); + if (matches == 3) { + parser_state++; + } else { + if (DEBUG) + fprintf(stderr,"BPyMenus error: Wrong 'Blender' line: %s\n",fname); + parser_state = 0; + } + break; + + case 5: /* Group: 'group name' */ + matches = sscanf(line, "%[^']'%[^']'%c", head, middle, tail); + if ((matches == 3) && (strstr(head, "Group:") != NULL)) { + script_group = bpymenu_group_atoi(middle); + if (script_group < 0) { + if (DEBUG) + fprintf(stderr, "BPyMenus error: Unknown group \"%s\": %s\n", + middle, fname); + parser_state = 0; + } + + else { /* register script */ + scriptMenu = bpymenu_AddEntry((short)script_group, + (short int)script_version, script_name, fname, is_userdir,NULL); + if (scriptMenu == NULL) { + if (DEBUG) + fprintf(stderr, + "BPyMenus error: Couldn't create entry for: %s\n", fname); + parser_state = 0; + } else { + parser_state++; + } + } + + } else { + if (DEBUG) + fprintf(stderr, "BPyMenus error: Wrong 'Group' line: %s\n",fname); + parser_state = 0; + } + break; + + case 6: /* optional elements */ + /* Submenu: 'submenu name' related_1word_arg */ + matches = sscanf(line, "%[^']'%[^']'%s\n", head, middle, tail); + if ((matches == 3) && (strstr(head, "Submenu:") != NULL)) { + bpymenu_AddSubEntry(scriptMenu, middle, tail); + } else { + /* Tooltip: 'tooltip for the menu */ + matches = sscanf(line, "%[^']'%[^']'%c", head, middle, tail); + if ((matches == 3) && ((strstr(head, "Tooltip:") != NULL) || + (strstr(head, "Tip:") != NULL))) { + bpymenu_set_tooltip(scriptMenu, middle); + } + parser_state = 0; + } + break; + + default: + parser_state = 0; + break; + } + } + } + + else { /* shouldn't happen, it's checked in bpymenus_ParseDir */ + if (DEBUG) + fprintf(stderr, "BPyMenus error: Couldn't open %s.\n", fname); + return -1; + } + + return 0; +} + +/* bpymenu_ParseDir: + * recursively scans folders looking for scripts to register. + * + * This function scans the scripts directory looking for .py files with the + * right header and menu info. + * - is_userdir defines if the script is in the default scripts dir or the + * user defined one (U.pythondir: is_userdir == 1); + * - parentdir is the parent dir name to store as part of the script filename, + * if we're down a subdir. + * Speed is important. + */ +static int bpymenu_ParseDir(char *dirname, char *parentdir, int is_userdir ) +{ + DIR *dir; + FILE *file = NULL; + struct dirent *de; + struct stat status; + char *file_extension; + char path[FILE_MAX]; + char subdir[FILE_MAX]; + char *s = NULL; + + dir = opendir(dirname); + + if (dir != NULL) { + while ((de = readdir(dir)) != NULL) { + + /* skip files and dirs starting with '.' or 'bpy' */ + if ((de->d_name[0] == '.') || !strncmp(de->d_name, "bpy", 3)) { + continue; + } + + BLI_make_file_string("/", path, dirname, de->d_name); + + if (stat(path, &status) != 0) { + if (DEBUG) + fprintf(stderr, "stat %s failed: %s\n", path, strerror(errno)); + } + + if (S_ISREG(status.st_mode)) { /* is file */ + + file_extension = strstr(de->d_name, ".py"); + + if (file_extension && *(file_extension + 3) == '\0') { + file = fopen(path, "rb"); + + if (file) { + s = de->d_name; + if (parentdir) { + /* Join parentdir and de->d_name */ + BLI_join_dirfile(subdir, parentdir, de->d_name); + + s = subdir; + } + bpymenu_ParseFile(file, s, is_userdir); + fclose(file); + } + + else { + if (DEBUG) + fprintf(stderr, "BPyMenus error: Couldn't open %s.\n", path); + } + } + } + + else if (S_ISDIR(status.st_mode)) { /* is subdir */ + Dirs_Number++; + Dir_Depth++; + if (Dirs_Number > MAX_DIR_NUMBER) { + if (DEBUG) { + fprintf(stderr, "BPyMenus error: too many subdirs.\n"); + } + closedir(dir); + return -1; + } + else if (Dir_Depth > MAX_DIR_DEPTH) { + if (DEBUG) + fprintf(stderr, + "BPyMenus error: max depth reached traversing dir tree.\n"); + closedir(dir); + return -1; + } + s = de->d_name; + if (parentdir) { + /* Join parentdir and de->d_name */ + BLI_join_dirfile(subdir, parentdir, de->d_name); + s = subdir; + } + if (bpymenu_ParseDir(path, s, is_userdir) == -1) { + closedir(dir); + return -1; + } + Dir_Depth--; + } + + } + closedir(dir); + } + + else { /* open directory stream failed */ + if (DEBUG) + fprintf(stderr, "opendir %s failed: %s\n", dirname, strerror(errno)); + return -1; + } + + return 0; +} + +static int bpymenu_GetStatMTime( char *name, int is_file, time_t * mtime ) +{ + struct stat st; + int result; + + result = stat( name, &st ); + + if( result == -1 ) + return -1; + + if( is_file ) { + if( !S_ISREG( st.st_mode ) ) + return -2; + } else if( !S_ISDIR( st.st_mode ) ) + return -2; + + *mtime = st.st_mtime; + + return 0; +} + +/* BPyMenu_Init: + * import the bpython menus data to Blender, either from: + * - the BPYMENU_DATAFILE file (?/.blender/Bpymenus) or + * - the scripts dir(s), case newer than the datafile (then update the file). + * then fill the bpymenu table with this data. + * if param usedir != 0, then the data is recreated from the dir(s) anyway. +*/ +int BPyMenu_Init( int usedir ) +{ + char fname[FILE_MAXDIR]; + char dirname[FILE_MAXDIR]; + char upythondir[FILE_MAXDIR]; + char *upydir = U.pythondir, *sdir = NULL; + time_t time_dir1 = 0, time_dir2 = 0, time_file = 0; + int stat_dir1 = 0, stat_dir2 = 0, stat_file = 0; + int i; + + DEBUG = G.f & G_DEBUG; /* is Blender in debug mode (started with -d) ? */ + + /* init global bpymenu table (it is a list of pointers to struct BPyMenus + * for each available group: import, export, etc.) */ + for( i = 0; i < PYMENU_TOTAL; i++ ) + BPyMenuTable[i] = NULL; + + if( DEBUG ) + fprintf(stdout, "\nRegistering scripts in Blender menus ...\n\n" ); + + if( U.pythondir[0] == '\0') { + upydir = NULL; + } + else if (strcmp(U.pythondir, "/") == 0 || strcmp(U.pythondir, "//") == 0) { + /* these are not accepted to prevent possible slight slowdowns on startup; + * they should not be used as user defined scripts dir, anyway, also from + * speed considerations, since they'd not be dedicated scripts dirs */ + if (DEBUG) fprintf(stderr, + "BPyMenus: invalid user defined Python scripts dir: \"/\" or \"//\".\n"); + upydir = NULL; + } + else { + BLI_strncpy(upythondir, upydir, FILE_MAXDIR); + BLI_convertstringcode(upythondir, G.sce, 0); + } + + sdir = bpy_gethome(1); + + if (sdir) { + BLI_strncpy(dirname, sdir, FILE_MAXDIR); + stat_dir1 = bpymenu_GetStatMTime( dirname, 0, &time_dir1 ); + + if( stat_dir1 < 0 ) { + time_dir1 = 0; + if( DEBUG ) { + fprintf(stderr, + "\nDefault scripts dir: %s:\n%s\n", dirname, strerror(errno)); + if( upydir ) + fprintf(stdout, + "Getting scripts menu data from user defined dir: %s.\n", + upythondir ); + } + } + } + else stat_dir1 = -1; + + if( upydir ) { + stat_dir2 = bpymenu_GetStatMTime( upythondir, 0, &time_dir2 ); + + if( stat_dir2 < 0 ) { + time_dir2 = 0; + upydir = NULL; + if( DEBUG ) + fprintf(stderr, "\nUser defined scripts dir: %s:\n%s.\n", + upythondir, strerror( errno ) ); + if( stat_dir1 < 0 ) { + if( DEBUG ) + fprintf(stderr, "\ +To have scripts in menus, please add them to the default scripts dir:\n\ +%s\n\ +and / or go to 'Info window -> File Paths tab' and set a valid path for\n\ +the user defined Python scripts dir.\n", dirname ); + return -1; + } + } + } + else stat_dir2 = -1; + + if( ( stat_dir1 < 0 ) && ( stat_dir2 < 0 ) ) { + if( DEBUG ) { + fprintf(stderr, "\nCannot register scripts in menus, no scripts dir" + " available.\nExpected default dir at: %s \n", dirname ); + } + return -1; + } + + if (usedir) stat_file = -1; + else { /* if we're not forced to use the dir */ + char *homedir = bpy_gethome(0); + + if (homedir) { + BLI_make_file_string( "/", fname, homedir, BPYMENU_DATAFILE ); + stat_file = bpymenu_GetStatMTime( fname, 1, &time_file ); + if( stat_file < 0 ) + time_file = 0; + + /* comparing dates */ + + if((stat_file == 0) + && (time_file > time_dir1) && (time_file > time_dir2)) + { /* file is newer */ + stat_file = bpymenu_CreateFromFile( ); /* -1 if an error occurred */ + if( !stat_file && DEBUG ) + fprintf(stdout, + "Getting menu data for scripts from file:\n%s\n\n", fname ); + } + else stat_file = -1; + } + else stat_file = -1; /* -1 to use dirs: didn't use file or it was corrupted */ + } + + if( stat_file == -1 ) { /* use dirs */ + if( DEBUG ) { + fprintf(stdout, + "Getting menu data for scripts from dir(s):\ndefault: %s\n", dirname ); + if( upydir ) + fprintf(stdout, "user defined: %s\n", upythondir ); + fprintf(stdout, "\n"); + } + if( stat_dir1 == 0 ) { + i = bpymenu_ParseDir( dirname, NULL, 0 ); + if (i == -1 && DEBUG) + fprintf(stderr, "Default scripts dir does not seem valid.\n\n"); + } + if( stat_dir2 == 0 ) { + BLI_strncpy(dirname, U.pythondir, FILE_MAXDIR); + BLI_convertstringcode(dirname, G.sce, 0); + i = bpymenu_ParseDir( dirname, NULL, 1 ); + if (i == -1 && DEBUG) + fprintf(stderr, "User defined scripts dir does not seem valid.\n\n"); + } + + /* check if we got any data */ + for( i = 0; i < PYMENU_TOTAL; i++ ) + if( BPyMenuTable[i] ) + break; + + /* if we got, recreate the file */ + if( i < PYMENU_TOTAL ) + bpymenu_WriteDataFile( ); + else if( DEBUG ) { + fprintf(stderr, "\n\ +Warning: Registering scripts in menus -- no info found.\n\ +Either your scripts dirs have no .py scripts or the scripts\n\ +don't have a header with registration data.\n\ +Default scripts dir is:\n\ +%s\n", dirname ); + if( upydir ) + fprintf(stderr, "User defined scripts dir is: %s\n", + upythondir ); + } + } + + return 0; +} diff --git a/source/blender/python/BPY_menus.h b/source/blender/python/BPY_menus.h new file mode 100644 index 00000000000..6cdea608b10 --- /dev/null +++ b/source/blender/python/BPY_menus.h @@ -0,0 +1,128 @@ +/* + * $Id: BPY_menus.h 12931 2007-12-17 18:20:48Z theeth $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * This is a new part of Blender. + * + * Contributor(s): Willian P. Germano, Matt Ebb + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** +*/ + +#ifndef BPY_MENUS_H +#define BPY_MENUS_H + +/* This header exposes BPyMenu related public declarations. The implementation + * adds 'dynamic' menus to Blender, letting scripts register themselves in any + * of a few pre-defined (trivial to upgrade) places in menus. These places or + * slots are called groups here (Import, Export, etc). This is how it works: + * - scripts at dirs user pref U.pythondir and .blender/scripts/ are scanned + * for registration info. + * - this data is also saved to a Bpymenus file at the user's .blender/ dir and + * only re-created when the scripts folder gets modified. + * - on start-up Blender uses this info to fill a table, which is used to + * create the menu entries when they are needed (see header_info.c or + * header_script.c, under source/blender/src/, for examples). +*/ + +/* These two structs hold py menu/submenu info. + * BPyMenu holds a script's name (as should appear in the menu) and filename, + * plus an optional list of submenus. Each submenu is related to a string + * (arg) that the script can get from the __script__ pydict, to know which + * submenu was chosen. */ + +typedef struct BPySubMenu { + char *name; + char *arg; + struct BPySubMenu *next; +} BPySubMenu; + +typedef struct BPyMenu { + char *name; + char *filename; + char *tooltip; + short version; /* Blender version */ + int dir; /* 0: default, 1: U.pythondir */ + struct BPySubMenu *submenus; + struct BPyMenu *next; +} BPyMenu; + +/* Scripts can be added to only a few pre-defined places in menus, like + * File->Import, File->Export, etc. (for speed and better control). + * To make a new menu 'slot' available for scripts: + * - add an entry to the enum below, before PYMENU_TOTAL, of course; + * - update the bpymenu_group_atoi() and BPyMenu_group_itoa() functions in + * BPY_menus.c; + * - add the necessary code to the header_***.c file in + * source/blender/src/, like done in header_info.c for import/export; +*/ +typedef enum { + PYMENU_ADD,/* creates new objects */ + PYMENU_ANIMATION, + PYMENU_EXPORT, + PYMENU_IMPORT, + PYMENU_MATERIALS, + PYMENU_MESH, + PYMENU_MISC, + PYMENU_OBJECT, + PYMENU_RENDER,/* exporters to external renderers */ + PYMENU_SYSTEM, + PYMENU_THEMES, + PYMENU_UV,/* UV editing tools, to go in UV/Image editor space, 'UV' menu */ + PYMENU_IMAGE,/* Image editing tools, to go in UV/Image editor space, 'Image' menu */ + PYMENU_WIZARDS,/* complex 'app' scripts */ + + /* entries put after Wizards don't appear at the Scripts win->Scripts menu; + * see define right below */ + + PYMENU_FACESELECT, + PYMENU_WEIGHTPAINT, + PYMENU_VERTEXPAINT, + PYMENU_UVCALCULATION, + PYMENU_ARMATURE, + PYMENU_SCRIPTTEMPLATE, + PYMENU_HELP,/*Main Help menu items - prob best to leave for 'official' ones*/ + PYMENU_HELPSYSTEM,/* Resources, troubleshooting, system tools */ + PYMENU_HELPWEBSITES,/* Help -> Websites submenu */ + PYMENU_MESHFACEKEY, /* face key in mesh editmode */ + PYMENU_ADDMESH, /* adds mesh */ + PYMENU_TOTAL +} PYMENUHOOKS; + +#define PYMENU_SCRIPTS_MENU_TOTAL (PYMENU_WIZARDS + 1) + +/* BPyMenuTable holds all registered pymenus, as linked lists for each menu + * where they can appear (see PYMENUHOOKS enum above). +*/ +extern BPyMenu *BPyMenuTable[]; /* defined in BPY_menus.c */ + +/* public functions: */ +int BPyMenu_Init( int usedir ); +void BPyMenu_RemoveAllEntries( void ); +void BPyMenu_PrintAllEntries( void ); +char *BPyMenu_CreatePupmenuStr( BPyMenu * pym, short group ); +char *BPyMenu_group_itoa( short group ); +struct BPyMenu *BPyMenu_GetEntry( short group, short pos ); + +#endif /* BPY_MENUS_H */ diff --git a/source/blender/python/Makefile b/source/blender/python/Makefile new file mode 100644 index 00000000000..43b6f91369b --- /dev/null +++ b/source/blender/python/Makefile @@ -0,0 +1,34 @@ +# +# $Id$ +# +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) Blender Foundation. +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): none yet. +# +# ***** END GPL LICENSE BLOCK ***** +# +# Bounces make to subdirectories. + +SOURCEDIR = source/blender/python +DIRS = intern generic + +include nan_subdirs.mk diff --git a/source/blender/python/epy_doc_gen.py b/source/blender/python/epy_doc_gen.py new file mode 100644 index 00000000000..f16c7504cb2 --- /dev/null +++ b/source/blender/python/epy_doc_gen.py @@ -0,0 +1,753 @@ + # ***** BEGIN GPL LICENSE BLOCK ***** + # + # This program is free software; you can redistribute it and/or + # modify it under the terms of the GNU General Public License + # as published by the Free Software Foundation; either version 2 + # of the License, or (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + # GNU General Public License for more details. + # + # You should have received a copy of the GNU General Public License + # along with this program; if not, write to the Free Software Foundation, + # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + # + # Contributor(s): Campbell Barton + # + # #**** END GPL LICENSE BLOCK #**** + +script_help_msg = ''' +Usage, +run this script from blenders root path once you have compiled blender + ./blender.bin -P source/blender/python/epy_doc_gen.py + +This will generate python files in "./source/blender/python/doc/bpy" +Generate html docs by running... + + epydoc source/blender/python/doc/bpy/ -v \\ + -o source/blender/python/doc/html \\ + --inheritance=included \\ + --no-sourcecode \\ + --graph=classtree \\ + --graph-font-size=8 + +''' + +# if you dont have graphvis installed ommit the --graph arg. + +# GLOBALS['BASEDIR'] = './source/blender/python/doc' + +import os + +SUBMODULES = {} +INIT_SUBMODULES = {} # store initialized files + +INIT_SUBMODULES_IMPORTS = {} # dont import the same module twice + +def append_package(package_path, mod_name): + + init_path = os.path.join(os.path.dirname(package_path), "__init__.py") + + # avoid double ups + if mod_name: + imports = INIT_SUBMODULES_IMPORTS.setdefault(init_path, []) + if mod_name in imports: + return + imports.append(mod_name) + + try: + os.makedirs(os.path.dirname(init_path)) # make the dirs if they are not there + except: + pass + + # Open the new file for the first time, otherwise keep it open. + f = INIT_SUBMODULES.get(init_path) + if f == None: + f = INIT_SUBMODULES[init_path] = open(init_path, 'w') + + if mod_name: + f.write("import %s\n" % mod_name) + + return f + +def append_package_recursive(package_path, BASEPATH): + ''' + assume the last item of package_path will be a file (not a dir thats created) + ''' + + package_path = os.path.splitext(package_path)[0] # incase of .py + + try: + os.makedirs(os.path.join(BASEPATH, os.path.dirname(package_path))) # make the dirs if they are not there + except: + pass + + new_path = BASEPATH + + for mod_name in package_path.split(os.sep): + init_path = os.path.join(new_path, "__init__.py") + new_path = os.path.join(new_path, mod_name) + append_package(init_path, mod_name) + + +def open_submodule(subpath, BASEPATH): + ''' + This is a utility function that lets us quickly add submodules + ''' + + # create all the package paths leading up to this module + append_package_recursive(subpath, BASEPATH) + + module_name = os.path.basename( os.path.splitext(subpath)[0] ) + mod_path = os.path.join(BASEPATH, subpath) + + # Open the new file for the first time, otherwise keep it open. + f = SUBMODULES.get(mod_path) + if f == None: + f = SUBMODULES[mod_path] = open(mod_path, 'w') + + f = open(mod_path, 'w') + return f + +def close_all(): + for files in (INIT_SUBMODULES.values(), SUBMODULES.values()): + for f in files: + if f.name.endswith('.py'): + f_name = f.name + f.close() + + f = open(f_name, 'a') + f.write("\ndel __package__\n") # annoying, no need do show this + + + f.close() + + +def range_str(val): + if val < -10000000: return '-inf' + if val > 10000000: return 'inf' + if type(val)==float: + return '%g' % val + else: + return str(val) + +def get_array_str(length): + if length > 0: return ' array of %d items' % length + else: return '' + +def full_rna_struct_path(rna_struct): + ''' + Needed when referencing one struct from another + ''' + nested = rna_struct.nested + if nested: + return "%s.%s" % (full_rna_struct_path(nested), rna_struct.identifier) + else: + return rna_struct.identifier + +def rna_id_ignore(rna_id): + if rna_id == "rna_type": + return True + + if "_OT_" in rna_id: + return True + if "_MT_" in rna_id: + return True + if "_PT_" in rna_id: + return True + + return False + +def write_func(rna, ident, out, func_type): + # Keyword attributes + kw_args = [] # "foo = 1", "bar=0.5", "spam='ENUM'" + kw_arg_attrs = [] # "@type mode: int" + + rna_struct= rna.rna_type + + # Operators and functions work differently + if func_type=='OPERATOR': + rna_func_name = rna_struct.identifier.split("_OT_")[-1] + rna_func_desc = rna_struct.description.strip() + items = rna_struct.properties.items() + else: + rna_func_name = rna.identifier + rna_func_desc = rna.description.strip() + items = rna.parameters.items() + + + for rna_prop_identifier, rna_prop in items: + if rna_id_ignore(rna_prop_identifier): + continue + + # clear vars + val = val_error = val_str = rna_prop_type = None + + # ['rna_type', 'name', 'array_length', 'description', 'hard_max', 'hard_min', 'identifier', 'precision', 'readonly', 'soft_max', 'soft_min', 'step', 'subtype', 'type'] + #rna_prop= op_rna.rna_type.properties[attr] + rna_prop_type = rna_prop.type.lower() # enum, float, int, boolean + + + # only for rna functions, operators should not get pointers as args + if rna_prop_type=='pointer': + rna_prop_type_refine = "L{%s}" % rna_prop.fixed_type.identifier + else: + # Collections/Arrays can have a srna type + rna_prop_srna_type = rna_prop.srna + if rna_prop_srna_type: + print(rna_prop_srna_type.identifier) + rna_prop_type_refine = "L{%s}" % rna_prop_srna_type.identifier + else: + rna_prop_type_refine = rna_prop_type + + del rna_prop_srna_type + + + try: length = rna_prop.array_length + except: length = 0 + + array_str = get_array_str(length) + + if rna_prop.use_return: + kw_type_str= "@rtype: %s%s" % (rna_prop_type_refine, array_str) + kw_param_str= "@return: %s" % (rna_prop.description.strip()) + else: + kw_type_str= "@type %s: %s%s" % (rna_prop_identifier, rna_prop_type_refine, array_str) + kw_param_str= "@param %s: %s" % (rna_prop_identifier, rna_prop.description.strip()) + + kw_param_set = False + + if func_type=='OPERATOR': + try: + val = getattr(rna, rna_prop_identifier) + val_error = False + except: + val = "'<UNDEFINED>'" + val_error = True + + + if val_error: + val_str = val + elif rna_prop_type=='float': + if length==0: + val_str= '%g' % val + if '.' not in val_str and '-' not in val_str: # value could be 1e-05 + val_str += '.0' + else: + # array + val_str = str(tuple(val)) + + kw_param_str += (' in (%s, %s)' % (range_str(rna_prop.hard_min), range_str(rna_prop.hard_max))) + kw_param_set= True + + elif rna_prop_type=='int': + if length==0: + val_str='%d' % val + else: + val_str = str(tuple(val)) + + # print(dir(rna_prop)) + kw_param_str += (' in (%s, %s)' % (range_str(rna_prop.hard_min), range_str(rna_prop.hard_max))) + # These strings dont have a max length??? + #kw_param_str += ' (maximum length of %s)' % (rna_prop.max_length) + kw_param_set= True + + elif rna_prop_type=='boolean': + if length==0: + if val: val_str='True' + else: val_str='False' + else: + val_str = str(tuple(val)) + + elif rna_prop_type=='enum': + # no array here? + val_str="'%s'" % val + # Too cramped + kw_param_str += (' in (%s)' % ', '.join(rna_prop.items.keys())) + + kw_param_set= True + + elif rna_prop_type=='string': + # no array here? + val_str='"%s"' % val + + # todo - collection - array + # print (rna_prop.type) + + kw_args.append('%s = %s' % (rna_prop_identifier, val_str)) + + # stora + else: + # currently functions dont have a default value + if not rna_prop.use_return: + kw_args.append('%s' % (rna_prop_identifier)) + else: + kw_param_set = True + + + # Same for operators and functions + kw_arg_attrs.append(kw_type_str) + if kw_param_set: + kw_arg_attrs.append(kw_param_str) + + + + out.write(ident+'def %s(%s):\n' % (rna_func_name, ', '.join(kw_args))) + out.write(ident+'\t"""\n') + + # Descriptions could be multiline + for rna_func_desc_line in rna_func_desc.split('\n'): + out.write(ident+'\t%s\n' % rna_func_desc_line.strip()) + + for desc in kw_arg_attrs: + out.write(ident+'\t%s\n' % desc) + + # out.write(ident+'\t@rtype: None\n') # implicit + out.write(ident+'\t"""\n') + + + +def rna2epy(BASEPATH): + + # Use for faster lookups + # use rna_struct.identifier as the key for each dict + rna_struct_dict = {} # store identifier:rna lookups + rna_full_path_dict = {} # store the result of full_rna_struct_path(rna_struct) + rna_children_dict = {} # store all rna_structs nested from here + rna_references_dict = {} # store a list of rna path strings that reference this type + rna_functions_dict = {} # store all functions directly in this type (not inherited) + rna_words = set() + + # def write_func(rna_func, ident): + + + def write_struct(rna_struct, ident): + identifier = rna_struct.identifier + + rna_base = rna_struct.base + + if rna_base: + out.write(ident+ 'class %s(%s):\n' % (identifier, rna_base.identifier)) + rna_base_prop_keys = rna_base.properties.keys() # could be cached + rna_base_func_keys = [f.identifier for f in rna_base.functions] + else: + out.write(ident+ 'class %s:\n' % identifier) + rna_base_prop_keys = [] + rna_base_func_keys = [] + + out.write(ident+ '\t"""\n') + + title = 'The %s Object' % rna_struct.name + description = rna_struct.description.strip() + out.write(ident+ '\t%s\n' % title) + out.write(ident+ '\t%s\n' % ('=' * len(title))) + out.write(ident+ '\t\t%s\n' % description) + rna_words.update(description.split()) + + + # For convenience, give a list of all places were used. + rna_refs= rna_references_dict[identifier] + + if rna_refs: + out.write(ident+ '\t\t\n') + out.write(ident+ '\t\tReferences\n') + out.write(ident+ '\t\t==========\n') + + for rna_ref_string in rna_refs: + out.write(ident+ '\t\t\t- L{%s}\n' % rna_ref_string) + + out.write(ident+ '\t\t\n') + + else: + out.write(ident+ '\t\t\n') + out.write(ident+ '\t\t(no references to this struct found)\n') + out.write(ident+ '\t\t\n') + + for rna_prop_identifier, rna_prop in rna_struct.properties.items(): + + if rna_prop_identifier=='RNA': continue + if rna_id_ignore(rna_prop_identifier): continue + if rna_prop_identifier in rna_base_prop_keys: continue # does this prop exist in our parent class, if so skip + + rna_desc = rna_prop.description.strip() + + if rna_desc: rna_words.update(rna_desc.split()) + if not rna_desc: rna_desc = rna_prop.name + if not rna_desc: rna_desc = 'Note - No documentation for this property!' + + rna_prop_type = rna_prop.type.lower() + + if rna_prop_type=='collection': collection_str = 'Collection of ' + else: collection_str = '' + + # some collections have a srna for their own properties + # TODO - arrays, however this isnt used yet + rna_prop_srna_type = rna_prop.srna + if rna_prop_srna_type: + collection_str = "L{%s} %s" % (rna_prop_srna_type.identifier, collection_str) + del rna_prop_srna_type + + try: rna_prop_ptr = rna_prop.fixed_type + except: rna_prop_ptr = None + + try: length = rna_prop.array_length + except: length = 0 + + array_str = get_array_str(length) + + if rna_prop.editable: readonly_str = '' + else: readonly_str = ' (readonly)' + + if rna_prop_ptr: # Use the pointer type + out.write(ident+ '\t@ivar %s: %s\n' % (rna_prop_identifier, rna_desc)) + out.write(ident+ '\t@type %s: %sL{%s}%s%s\n' % (rna_prop_identifier, collection_str, rna_prop_ptr.identifier, array_str, readonly_str)) + else: + if rna_prop_type == 'enum': + if 0: + out.write(ident+ '\t@ivar %s: %s in (%s)\n' % (rna_prop_identifier, rna_desc, ', '.join(rna_prop.items.keys()))) + else: + out.write(ident+ '\t@ivar %s: %s in...\n' % (rna_prop_identifier, rna_desc)) + for e, e_rna_prop in rna_prop.items.items(): + #out.write(ident+ '\t\t- %s: %s\n' % (e, e_rna_prop.description)) # XXX - segfaults, FIXME + out.write(ident+ '\t\t- %s\n' % e) + + out.write(ident+ '\t@type %s: %s%s%s\n' % (rna_prop_identifier, rna_prop_type, array_str, readonly_str)) + elif rna_prop_type == 'int' or rna_prop_type == 'float': + out.write(ident+ '\t@ivar %s: %s\n' % (rna_prop_identifier, rna_desc)) + out.write(ident+ '\t@type %s: %s%s%s in [%s, %s]\n' % (rna_prop_identifier, rna_prop_type, array_str, readonly_str, range_str(rna_prop.hard_min), range_str(rna_prop.hard_max) )) + elif rna_prop_type == 'string': + out.write(ident+ '\t@ivar %s: %s (maximum length of %s)\n' % (rna_prop_identifier, rna_desc, rna_prop.max_length)) + out.write(ident+ '\t@type %s: %s%s%s\n' % (rna_prop_identifier, rna_prop_type, array_str, readonly_str)) + else: + out.write(ident+ '\t@ivar %s: %s\n' % (rna_prop_identifier, rna_desc)) + out.write(ident+ '\t@type %s: %s%s%s\n' % (rna_prop_identifier, rna_prop_type, array_str, readonly_str)) + + + out.write(ident+ '\t"""\n\n') + + + # Write functions + # for rna_func in rna_struct.functions: # Better ignore inherited (line below) + for rna_func in rna_functions_dict[identifier]: + if rna_func not in rna_base_func_keys: + write_func(rna_func, ident+'\t', out, 'FUNCTION') + + out.write('\n') + + # Now write children recursively + for child in rna_children_dict[identifier]: + write_struct(child, ident + '\t') + + + + # out = open(target_path, 'w') + out = open_submodule("types.py", BASEPATH) # bpy.types + + def base_id(rna_struct): + try: return rna_struct.base.identifier + except: return '' # invalid id + + #structs = [(base_id(rna_struct), rna_struct.identifier, rna_struct) for rna_struct in bpy.doc.structs.values()] + ''' + structs = [] + for rna_struct in bpy.doc.structs.values(): + structs.append( (base_id(rna_struct), rna_struct.identifier, rna_struct) ) + ''' + structs = [] + for rna_type_name in dir(bpy.types): + rna_type = getattr(bpy.types, rna_type_name) + + try: rna_struct = rna_type.bl_rna + except: rna_struct = None + + if rna_struct: + #if not rna_type_name.startswith('__'): + + identifier = rna_struct.identifier + + if not rna_id_ignore(identifier): + structs.append( (base_id(rna_struct), identifier, rna_struct) ) + + # Simple lookup + rna_struct_dict[identifier] = rna_struct + + # Store full rna path 'GameObjectSettings' -> 'Object.GameObjectSettings' + rna_full_path_dict[identifier] = full_rna_struct_path(rna_struct) + + # Store a list of functions, remove inherited later + rna_functions_dict[identifier]= list(rna_struct.functions) + + + # fill in these later + rna_children_dict[identifier]= [] + rna_references_dict[identifier]= [] + + + else: + print("Ignoring", rna_type_name) + + + # Sucks but we need to copy this so we can check original parent functions + rna_functions_dict__copy = {} + for key, val in rna_functions_dict.items(): + rna_functions_dict__copy[key] = val[:] + + + structs.sort() # not needed but speeds up sort below, setting items without an inheritance first + + # Arrange so classes are always defined in the correct order + deps_ok = False + while deps_ok == False: + deps_ok = True + rna_done = set() + + for i, (rna_base, identifier, rna_struct) in enumerate(structs): + + rna_done.add(identifier) + + if rna_base and rna_base not in rna_done: + deps_ok = False + data = structs.pop(i) + ok = False + while i < len(structs): + if structs[i][1]==rna_base: + structs.insert(i+1, data) # insert after the item we depend on. + ok = True + break + i+=1 + + if not ok: + print('Dependancy "%s" could not be found for "%s"' % (identifier, rna_base)) + + break + + # Done ordering structs + + + # precalc vars to avoid a lot of looping + for (rna_base, identifier, rna_struct) in structs: + + if rna_base: + rna_base_prop_keys = rna_struct_dict[rna_base].properties.keys() # could cache + rna_base_func_keys = [f.identifier for f in rna_struct_dict[rna_base].functions] + else: + rna_base_prop_keys = [] + rna_base_func_keys= [] + + # rna_struct_path = full_rna_struct_path(rna_struct) + rna_struct_path = rna_full_path_dict[identifier] + + for rna_prop_identifier, rna_prop in rna_struct.properties.items(): + + if rna_prop_identifier=='RNA': continue + if rna_id_ignore(rna_prop_identifier): continue + if rna_prop_identifier in rna_base_prop_keys: continue + + + for rna_prop_ptr in (getattr(rna_prop, "fixed_type", None), getattr(rna_prop, "srna", None)): + # Does this property point to me? + if rna_prop_ptr: + rna_references_dict[rna_prop_ptr.identifier].append( "%s.%s" % (rna_struct_path, rna_prop_identifier) ) + + for rna_func in rna_struct.functions: + for rna_prop_identifier, rna_prop in rna_func.parameters.items(): + + if rna_prop_identifier=='RNA': continue + if rna_id_ignore(rna_prop_identifier): continue + if rna_prop_identifier in rna_base_func_keys: continue + + + try: rna_prop_ptr = rna_prop.fixed_type + except: rna_prop_ptr = None + + # Does this property point to me? + if rna_prop_ptr: + rna_references_dict[rna_prop_ptr.identifier].append( "%s.%s" % (rna_struct_path, rna_func.identifier) ) + + + # Store nested children + nested = rna_struct.nested + if nested: + rna_children_dict[nested.identifier].append(rna_struct) + + + if rna_base: + rna_funcs = rna_functions_dict[identifier] + if rna_funcs: + # Remove inherited functions if we have any + rna_base_funcs = rna_functions_dict__copy[rna_base] + rna_funcs[:] = [f for f in rna_funcs if f not in rna_base_funcs] + + rna_functions_dict__copy.clear() + del rna_functions_dict__copy + + # Sort the refs, just reads nicer + for rna_refs in rna_references_dict.values(): + rna_refs.sort() + + for (rna_base, identifier, rna_struct) in structs: + if rna_struct.nested: + continue + + write_struct(rna_struct, '') + + + out.write('\n') + out.close() + + # # We could also just run.... + # os.system('epydoc source/blender/python/doc/rna.py -o ./source/blender/python/doc/html -v') + + target_path = os.path.join(BASEPATH, "dump.py") # XXX - used for other funcs + + # Write graphviz + out= open(target_path.replace('.py', '.dot'), 'w') + out.write('digraph "rna data api" {\n') + out.write('\tnode [style=filled, shape = "box"];\n') + out.write('\toverlap=false;\n') + out.write('\trankdir = LR;\n') + out.write('\tsplines=true;\n') + out.write('\tratio=auto;\n') + + + + out.write('\tsize="10,10"\n') + #out.write('\tpage="8.5,11"\n') + #out.write('\tcenter=""\n') + + def isop(rna_struct): + return '_OT_' in rna_struct.identifier + + + for (rna_base, identifier, rna_struct) in structs: + if isop(rna_struct): + continue + + base = rna_struct.base + + + out.write('\t"%s";\n' % identifier) + + for (rna_base, identifier, rna_struct) in structs: + + if isop(rna_struct): + continue + + base = rna_struct.base + + if base and not isop(base): + out.write('\t"%s" -> "%s" [label="(base)" weight=1.0];\n' % (base.identifier, identifier)) + + nested = rna_struct.nested + if nested and not isop(nested): + out.write('\t"%s" -> "%s" [label="(nested)" weight=1.0];\n' % (nested.identifier, identifier)) + + + + rna_refs= rna_references_dict[identifier] + + for rna_ref_string in rna_refs: + + if '_OT_' in rna_ref_string: + continue + + ref = rna_ref_string.split('.')[-2] + out.write('\t"%s" -> "%s" [label="%s" weight=0.01];\n' % (ref, identifier, rna_ref_string)) + + + out.write('}\n') + out.close() + + # # We could also just run.... + # os.system('dot source/blender/python/doc/rna.dot -Tsvg -o ./source/blender/python/doc/rna.svg') + + + out= open(target_path.replace('.py', '.words'), 'w') + rna_words = list(rna_words) + rna_words.sort() + for w in rna_words: + out.write('%s\n' % w) + + +def op2epy(BASEPATH): + # out = open(target_path, 'w') + + op_mods = dir(bpy.ops) + op_mods.remove('add') + op_mods.remove('remove') + + for op_mod_name in sorted(op_mods): + if op_mod_name.startswith('__'): + continue + + # open the submodule + mod_path = os.path.join("ops", op_mod_name + ".py") + out = open_submodule(mod_path, BASEPATH) + + + op_mod = getattr(bpy.ops, op_mod_name) + operators = dir(op_mod) + for op in sorted(operators): + # rna = getattr(bpy.types, op).bl_rna + rna = getattr(op_mod, op).get_rna() + write_func(rna, '', out, 'OPERATOR') + + out.write('\n') + out.close() + +def misc2epy(BASEPATH): + ''' + Hard coded modules, try to avoid adding stuff here + ''' + + f = append_package(os.path.join(BASEPATH, ""), ""); # add a slash on the end of the base path + f.write(''' +""" +@type data: L{bpy.types.Main} +@var data: blender data is accessed from here +""" +''') + + f = open_submodule("props.py", BASEPATH) + f.write(''' +MAX_INT= 2**31 +MAX_FLOAT= 1e+37 +def BoolProperty(attr, name="", description="", default=False): + """ + return a new bool property + """ +def IntProperty(attr, name="", description="", min=-MAX_INT, max=MAX_INT, soft_min=-MAX_INT, soft_max=MAX_INT, default=0): + """ + return a new int property + """ +def FloatProperty(attr, name="", description="", min=-MAX_FLOAT, max=MAX_FLOAT, soft_min=-MAX_FLOAT, soft_max=MAX_FLOAT, default=0.0): + """ + return a new float property + """ +def StringProperty(attr, name="", description="", maxlen=0, default=""): + """ + return a new string property + """ +def EnumProperty(attr, items, name="", description="", default=""): + """ + return a new enum property + """ +''') + + +if __name__ == '__main__': + if 'bpy' not in dir(): + print("\nError, this script must run from inside blender2.5") + print(script_help_msg) + else: + misc2epy('source/blender/python/doc/bpy') # first to write in info in some of the modules. + rna2epy('source/blender/python/doc/bpy') + op2epy('source/blender/python/doc/bpy') + + + close_all() + + import sys + sys.exit() diff --git a/source/blender/python/generic/bpy_internal_import.h b/source/blender/python/generic/bpy_internal_import.h index 0ef31229f8d..eb49a3e7c6c 100644 --- a/source/blender/python/generic/bpy_internal_import.h +++ b/source/blender/python/generic/bpy_internal_import.h @@ -1,5 +1,5 @@ /* - * $Id$ + * $Id: bpy_internal_import.h 21094 2009-06-23 00:09:26Z gsrb3d $ * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or diff --git a/source/blender/python/intern/Makefile b/source/blender/python/intern/Makefile new file mode 100644 index 00000000000..6c0b7b4d14e --- /dev/null +++ b/source/blender/python/intern/Makefile @@ -0,0 +1,67 @@ +# +# $Id$ +# +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): none yet. +# +# ***** END GPL LICENSE BLOCK ***** +# +# + +LIBNAME = python +DIR = $(OCGDIR)/blender/$(LIBNAME) + +include nan_compile.mk + +CFLAGS += $(LEVEL_1_C_WARNINGS) + +# OpenGL and Python +CPPFLAGS += $(OGL_CPPFLAGS) +CPPFLAGS += -I$(NAN_GLEW)/include +CPPFLAGS += -I$(NAN_PYTHON)/include/python$(NAN_PYTHON_VERSION) + +# PreProcessor stuff + +CPPFLAGS += -I$(NAN_GHOST)/include +CPPFLAGS += $(NAN_SDLCFLAGS) + +# modules +CPPFLAGS += -I../../editors/include +CPPFLAGS += -I../../python +CPPFLAGS += -I../../makesdna +CPPFLAGS += -I../../makesrna +CPPFLAGS += -I../../blenlib +CPPFLAGS += -I../../blenkernel +CPPFLAGS += -I../../nodes +CPPFLAGS += -I../../imbuf +CPPFLAGS += -I../../blenloader +CPPFLAGS += -I../../windowmanager +CPPFLAGS += -I../../render/extern/include +CPPFLAGS += -I$(NAN_AUDASPACE)/include + +# path to the guarded memory allocator +CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include +CPPFLAGS += -I$(NAN_MEMUTIL)/include + +# path to our own headerfiles +CPPFLAGS += -I.. diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index 340e3f4ac57..906f6ccbca2 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -60,7 +60,7 @@ #include "../generic/blf_py_api.h" #include "../generic/IDProp.h" -#include "AUD_PyInit.h" +//#include "AUD_PyInit.h" PyObject *bpy_package_py= NULL; diff --git a/source/blender/python/intern/bpy_operator_wrap.c b/source/blender/python/intern/bpy_operator_wrap.c index e1ef8aa49c1..de29cb2aeac 100644 --- a/source/blender/python/intern/bpy_operator_wrap.c +++ b/source/blender/python/intern/bpy_operator_wrap.c @@ -1,4 +1,3 @@ - /* * $Id$ * diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h index ba19f155842..e9969277550 100644 --- a/source/blender/python/intern/bpy_rna.h +++ b/source/blender/python/intern/bpy_rna.h @@ -31,6 +31,7 @@ /* --- bpy build options --- */ #ifdef WITH_PYTHON_SAFETY +#include <Python.h> /* play it safe and keep optional for now, need to test further now this affects looping on 10000's of verts for eg. */ #define USE_WEAKREFS diff --git a/source/blender/python/intern/bpy_ui.c b/source/blender/python/intern/bpy_ui.c new file mode 100644 index 00000000000..ab50ebd8bd5 --- /dev/null +++ b/source/blender/python/intern/bpy_ui.c @@ -0,0 +1,69 @@ +/** + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "bpy_ui.h" +#include "bpy_util.h" +#include "bpy_rna.h" /* for rna buttons */ +#include "bpy_operator.h" /* for setting button operator properties */ + +#include "WM_types.h" /* for WM_OP_INVOKE_DEFAULT & friends */ + +#include "BLI_dynstr.h" + +#include "MEM_guardedalloc.h" +#include "BKE_global.h" /* evil G.* */ +#include "BKE_context.h" + +#include "DNA_screen_types.h" +#include "DNA_space_types.h" /* only for SpaceLink */ +#include "UI_interface.h" +#include "WM_api.h" + +/* Dummy Module, may want to include non RNA UI functions here, else it can be removed */ + +static struct PyMethodDef ui_methods[] = { + {NULL, NULL, 0, NULL} +}; + +static struct PyModuleDef ui_module = { + PyModuleDef_HEAD_INIT, + "_bpy.ui", + "", + -1,/* multiple "initialization" just copies the module dict. */ + ui_methods, + NULL, NULL, NULL, NULL +}; + +PyObject *BPY_ui_module( void ) +{ + PyObject *submodule; + submodule= PyModule_Create(&ui_module); + + /* INCREF since its its assumed that all these functions return the + * module with a new ref like PyDict_New, since they are passed to + * PyModule_AddObject which steals a ref */ + Py_INCREF(submodule); + + return submodule; +} diff --git a/source/blender/python/intern/bpy_ui.h b/source/blender/python/intern/bpy_ui.h new file mode 100644 index 00000000000..95afa1f08c8 --- /dev/null +++ b/source/blender/python/intern/bpy_ui.h @@ -0,0 +1,31 @@ +/** + * $Id: bpy_ui.h 21094 2009-06-23 00:09:26Z gsrb3d $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ +#ifndef BPY_UI_H__ +#define BPY_UI_H__ + +#include <Python.h> + +PyObject *BPY_ui_module( void ); + +#endif diff --git a/source/blender/python/simple_enum_gen.py b/source/blender/python/simple_enum_gen.py index bc7a2df9fb6..a536f04ecde 100644 --- a/source/blender/python/simple_enum_gen.py +++ b/source/blender/python/simple_enum_gen.py @@ -19,24 +19,24 @@ # #**** END GPL LICENSE BLOCK #**** defs = """ - SPACE_EMPTY, - SPACE_VIEW3D, - SPACE_IPO, - SPACE_OUTLINER, - SPACE_BUTS, - SPACE_FILE, + SPACE_EMPTY, + SPACE_VIEW3D, + SPACE_IPO, + SPACE_OUTLINER, + SPACE_BUTS, + SPACE_FILE, SPACE_IMAGE, - SPACE_INFO, - SPACE_SEQ, - SPACE_TEXT, - SPACE_IMASEL, - SPACE_SOUND, - SPACE_ACTION, - SPACE_NLA, - SPACE_SCRIPT, - SPACE_TIME, - SPACE_NODE, - SPACEICONMAX + SPACE_INFO, + SPACE_SEQ, + SPACE_TEXT, + SPACE_IMASEL, + SPACE_SOUND, + SPACE_ACTION, + SPACE_NLA, + SPACE_SCRIPT, + SPACE_TIME, + SPACE_NODE, + SPACEICONMAX """ print '\tmod = PyModule_New("dummy");' @@ -44,21 +44,21 @@ print '\tPyModule_AddObject( submodule, "key", mod );' for d in defs.split('\n'): - d = d.replace(',', ' ') - w = d.split() + d = d.replace(',', ' ') + w = d.split() - if not w: - continue + if not w: + continue - try: w.remove("#define") - except: pass + try: w.remove("#define") + except: pass - # print w + # print w - val = w[0] - py_val = w[0] + val = w[0] + py_val = w[0] - print '\tPyModule_AddObject( mod, "%s", PyLong_FromSize_t(%s) );' % (val, py_val) + print '\tPyModule_AddObject( mod, "%s", PyLong_FromSize_t(%s) );' % (val, py_val) |