├── .gitignore ├── LICENSE ├── README.rst ├── getnaifspicetoolkit.py ├── mkwrapper.py ├── pyspice.c ├── pyspice.h ├── setup.py ├── spice ├── __init__.py ├── misc.py └── objects.py └── tests ├── test_exception.py └── test_pyspice.py /.gitignore: -------------------------------------------------------------------------------- 1 | spicemodule.c 2 | build 3 | *.pyc 4 | cspice 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005, Roberto Aguilar/JPL 2 | All rights reserved. 3 | 4 | Redistribution and use of this software in source and binary forms, with or 5 | without modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name, "Roberto Aguilar", "JPL", nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 23 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | SPICE toolkit Python Module 2 | =========================== 3 | 4 | Python wrapper around the NAIF CSPICE library. Released under the BSD license, see LICENSE for details. 5 | 6 | Building PySPICE 7 | ---------------- 8 | 9 | First, download the cspice toolkit and extract it to the directory "cspice" in 10 | this directory right alongside the setup.py file. Once the cspice source is 11 | there, run setup.py like so:: 12 | 13 | python setup.py build_ext 14 | 15 | Then install:: 16 | 17 | python setup.py install 18 | 19 | 64 bit vs 32 bit 20 | ---------------- 21 | CSPICE is published in both 64 and 32 bit versions. Make sure that you compile 22 | PySPICE with a Python bit architecture that fits to the CSPICE you have 23 | downloaded, otherwise you will get warnings at compile time (not so bad) and 24 | errors of missing links in the library at run time (basically, you can't *import 25 | spice*. 26 | 27 | Manual Instructions 28 | ------------------- 29 | Though it shouldn't be necessary, here are the old step-by-step instructions. 30 | 31 | In order to build this module, first generate the extension code using the 32 | mkwrapper.py script. This is done running mkwrapper.py with the path to the 33 | CSPICE toolkit directory as an argument and redirecting the output to 34 | "spicemodule.c":: 35 | 36 | python mkwrapper.py /path/to/cspice > spicemodule.c 37 | 38 | Once the C file is generated, the module can be compiled:: 39 | 40 | python setup.py build_ext -I/path/to/cspice/include -L/path/to/cspice/lib 41 | 42 | Then the module can be installed using:: 43 | 44 | python setup.py install --prefix=/installation/path 45 | 46 | If the installation path used is not standard, add the path to your 47 | ``PYTHONPATH`` environment variable. In bash:: 48 | 49 | export PYTHONPATH=/installation/path/lib/python/site-packages:${PYTHONPATH} 50 | 51 | or *csh:: 52 | 53 | setenv PYTHONPATH /installation/path/lib/python/site-packages:${PYTHONPATH} 54 | 55 | Usage 56 | ===== 57 | 58 | berto:~$ python 59 | Python 2.4.2 (#2, Sep 30 2005, 21:19:01) 60 | [GCC 4.0.2 20050808 (prerelease) (Ubuntu 4.0.1-4ubuntu8)] on linux2 61 | Type "help", "copyright", "credits" or "license" for more information. 62 | >>> from spice import * 63 | >>> furnsh("/home/berto/tmp/insitu/kernels/load.mk") 64 | >>> utc2et("2004-06-11T19:32:00") 65 | 140254384.18462521 66 | 67 | Some things to keep in mind 68 | --------------------------- 69 | 70 | The python wrapper drops the _c suffix in all function names, so the 71 | function utc2et_c becomes utc2et. 72 | 73 | Also, the CSPICE toolkit passes both inputs and outputs into a SPICE 74 | function:: 75 | 76 | SpiceDouble et; 77 | SpiceChar * utc = "2004-06-11T19:32:00"; 78 | 79 | utc2et_c(utc, &et); 80 | 81 | printf("et: %f\n", et); 82 | 83 | But, in Python, the outputs are returned:: 84 | 85 | utc = "2004-06-11T19:32:00" 86 | 87 | et = utc2et(utc) 88 | 89 | print "et: %f" % et 90 | 91 | If a function returns multiple values they are returned in a tuple:: 92 | 93 | target_pos, light_time = spkpos(target, sc_et, frame, aberration, sc_name) 94 | 95 | print "light time: %f" % light_time 96 | print "xyz: [%e, %e, %e]" % target_pos 97 | 98 | In the case above, the target position and light time are returned in a tuple. 99 | Additionally, target_pos itself is a tuple; its individual elements can be 100 | accessed like this:: 101 | 102 | print "x position: %d" % target_pos[0] 103 | 104 | Tuples act just like arrays. 105 | 106 | Enjoy! 107 | -------------------------------------------------------------------------------- /getnaifspicetoolkit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Select and extract JPL/NAIF SPICE toolkit for local OS and Architecture 4 | # 5 | # Run this command to extract appropriate JPL/NAIF SPICE toolkit 6 | # into ./cspice/: 7 | # 8 | # getnaiftoolkit.py extract 9 | # 10 | # Author: Brian Carcich, drbitboy@gmail.com 11 | # Original date: 2013-01-05 12 | # 13 | # Released under the BSD license, see LICENSE for details 14 | # 15 | # $Id$ 16 | 17 | """ 18 | getnaiftoolkit.py 19 | 20 | Extracts JPL/NAIF SPICE toolkit from compressed URL stream of 21 | 22 | cspice.tar.Z 23 | 24 | N.B. Untested for Windows/VisualC, cspice.zip 25 | 26 | Usage: 27 | 28 | % python getnaiftoolkit.py [extract] [topdir=subdir/] [test=MACH_OS_COMPILER_NNbit] 29 | 30 | ### default sys.argv[1] is tv => tar tvf -, so 31 | ### 32 | ### python getnaiftoolkit.py | more 33 | ### 34 | ### is probably a good idea 35 | 36 | In Python: 37 | 38 | import getnaiftoolkit 39 | getnaiftoolkit.main(['extract']) 40 | 41 | """ 42 | import re 43 | import os 44 | import sys 45 | import urllib 46 | import subprocess 47 | 48 | ######################################################################## 49 | def getnstkurl(force=None,log=False): 50 | """ 51 | Select URL of NAIF SPICE toolkit cspice.tar.Z suitable for 52 | local OS (os.uname()[0]) and architecture (os.uname()[-1]) 53 | 54 | OS: OSX; Cygwin; Linux; Windows; SunOS. 55 | 56 | MACHINE: i386; x86_64; PowerPC/PPC. 57 | 58 | Index of http://naif.jpl.nasa.gov/pub/naif/toolkit/C/: 59 | 60 | - Subdirectories: 61 | 62 | MacIntel_OSX_AppleC_32bit/ 63 | MacIntel_OSX_AppleC_64bit/ 64 | 65 | MacPPC_OSX_AppleC_32bit/ 66 | 67 | PC_Cygwin_GCC_32bit/ 68 | 69 | PC_Linux_GCC_32bit/ 70 | PC_Linux_GCC_64bit/ 71 | 72 | PC_Windows_VisualC_32bit/ 73 | PC_Windows_VisualC_64bit/ 74 | 75 | SunIntel_Solaris_SunC_32bit/ 76 | SunIntel_Solaris_SunC_64bit/ 77 | 78 | SunSPARC_Solaris_GCC_32bit/ 79 | SunSPARC_Solaris_GCC_64bit/ 80 | SunSPARC_Solaris_SunC_32bit/ 81 | SunSPARC_Solaris_SunC_64bit/ 82 | 83 | - under those directories are packages/cspice.tar.Z 84 | 85 | """ 86 | 87 | ### Convert possible alternate values that may be expected from os.uname() 88 | 89 | convert = dict( DARWIN='OSX' 90 | , POWERPC='PPC' 91 | , SOLARIS='SUNOS' 92 | , I486='I386' 93 | , I586='I386' 94 | , I686='I386' 95 | , I86PC='I386' 96 | ) 97 | 98 | ### Translatiions by OS and MACHINE to first and second elements of SPICE 99 | ### subdirectory name e.g. OSX and I386 => MacIntel_OSX; 100 | ### SUNOS and SUN4U => SunSPARC_Solaris 101 | ### 102 | 103 | dSys1 = dict( OSX=dict( I386='MacIntel', PPC='MacPPC', sis2='OSX', zSfx='tar.Z' ) 104 | , LINUX=dict( I386='PC', X86_64='PC', sis2='Linux', zSfx='tar.Z' ) 105 | , CYGWIN=dict( I386='PC', X86_64='PC', sis2='Cygwin', zSfx='tar.Z' ) 106 | , WINDOWS=dict( I386='PC', X86_64='PC', sis2='Windows', zSfx='zip' ) 107 | , SUNOS=dict( I386='SunIntel', SUN4U='SunSPARC', sis2='Solaris', zSfx='tar.Z' ) 108 | ) 109 | 110 | ### Suffix: 32bit or 64bit: 111 | 112 | lsmi=len(str(sys.maxint)) 113 | 114 | ### 32-bit architecture will have sys.maxint = 2^32 - 1 ~ 2G = 10 digits 115 | ### 64-bit architecture will have sys.maxint = 2^64 - 1 ~ 16E = 19 digits 116 | 117 | if lsmi<11: unbit='32bit' 118 | elif lsmi<20: unbit='64bit' 119 | 120 | ### Use os.uname() to get OS and MACHINE: 121 | 122 | un=os.uname() 123 | 124 | opsys = (''.join(un[0].split())).upper() 125 | machine = (''.join(un[-1].split())).upper() 126 | 127 | if opsys in convert: opsys=convert[opsys] 128 | if machine in convert: machine=convert[machine] 129 | 130 | ### Chop to six characters to simplify CYGWIN_NT... 131 | 132 | opsys=opsys[:6] 133 | 134 | if opsys=='OSX': 135 | compiler='AppleC' 136 | 137 | elif opsys=='WINDOWS': 138 | compiler='VisualC' 139 | 140 | elif opsys=='LINUX' or opsys=='CYGWIN': 141 | compiler='GCC' 142 | 143 | elif opsys=='SUNOS': 144 | 145 | ### For Solaris, assume SunC compiler ... 146 | compiler='SunC' 147 | 148 | ### ... unless gcc found in PATH 149 | for path in os.environ['PATH'].split(':'): 150 | if os.path.exists( os.path.join(path,'gcc')): 151 | compiler='GCC' 152 | break 153 | 154 | ### No toolkit exists for GCC on Solaris/x86, so force SunC 155 | if dSys1[opsys][machine] == 'SunIntel': compiler='SunC' 156 | 157 | ### Build the subdirectory name ... 158 | 159 | subdir='_'.join([ dSys1[opsys][machine], dSys1[opsys]['sis2'], compiler, unbit ]) 160 | 161 | fullurl = os.path.join( 'http://naif.jpl.nasa.gov/pub/naif/toolkit/C/', subdir, 'packages', 'cspice.'+ dSys1[opsys]['zSfx'] ) 162 | 163 | if not (force is None): 164 | M,O,C,B = [s.upper() for s in force.split('_')] 165 | oldurl = fullurl 166 | fullurl = os.path.join( 'http://naif.jpl.nasa.gov/pub/naif/toolkit/C/', force, 'packages', 'cspice.'+ dSys1[O]['zSfx'] ) 167 | if log: sys.stderr.write( '### Overriding %s with %s\n' % (oldurl,fullurl,) ) 168 | 169 | ### ... and return the full URL 170 | 171 | return fullurl 172 | 173 | ######################################################################## 174 | def main(argv): 175 | """ 176 | getnaiftoolkit.main() 177 | 178 | Use getnstkurl() above to stream cspice.tar.Z or .zip from JPL/NAIF 179 | website into 'gunzip | tar ?f - [-C subdir/] cspice/' for 180 | non-Windows systems, or into StringIO => ZipFile for Windows 181 | 182 | - do no use tar z.f - as it will not work on Solaris 183 | 184 | Arguments: 185 | 186 | argv: list, or tuple, of strings, equivalent to typical sys.argv 187 | 188 | - list - list cspice/ files in cspice.{tar.gz,zip} 189 | - extract - extract cspice/ files from cspice.{tar.gz,zip} 190 | - topdir=dir/subdir/ - extract files to .../ 191 | - test=MACH_OS_CC_NNbit - extract files to .../ 192 | """ 193 | 194 | actionOption='list' 195 | topdirOption='./' 196 | testOption=None 197 | 198 | for arg in argv: 199 | if arg=='extract': actionOption='extract' ; continue 200 | if arg=='list': actionOption='list' ; continue 201 | if arg[:7]=='topdir=': topdirOption=arg[7:] ; continue 202 | if arg[:5]=='test=': testOption=arg[5:] ; continue 203 | 204 | ### Get URL and open as stream 205 | 206 | nstkurl = getnstkurl(force=testOption,log=True) 207 | zurl = urllib.urlopen( nstkurl ) 208 | 209 | if nstkurl[-4:].lower()=='.zip': 210 | 211 | ### If URL is a .ZIP file, read entire file into StringIO buffer 212 | ### and use zipfile.ZipFile to extract cspice/ 213 | 214 | import zipfile 215 | from StringIO import StringIO 216 | 217 | sys.stderr.write( "### Downloading %s this may take a while ..." % (nstkurl,) ) 218 | sys.stderr.flush() 219 | 220 | zf = zipfile.ZipFile(StringIO(zurl.read())) 221 | 222 | sys.stderr.write( " download complete; %sing files from cspice/ ...\n" % (actionOption,) ) 223 | sys.stderr.flush() 224 | 225 | for info in zf.infolist(): 226 | filepath=info.filename 227 | if os.path.dirname(filepath)[:7]=='cspice/': 228 | if actionOption=='list': 229 | print( os.path.join( topdirOption, filepath ) ) 230 | elif actionOption=='extract': 231 | zf.extract(member=filepath,path=topdirOption) 232 | 233 | print( "### %sing complete" % (actionOption,) ) 234 | 235 | else: 236 | ### If URL is not a .ZIP file, assume it is .tar.Z, and 237 | ### extract data with pipe to subprocess ( gunzip | tar ..f - ) 238 | 239 | ### Build subprocess command 240 | 241 | tarAction=dict( list='tv', extract='x')[actionOption] 242 | cmd = '( gunzip | tar %sf - %s )' % (tarAction,'-C '+topdirOption,) 243 | 244 | sys.stderr.write( '### Executing command "%s" on URL \n### %s\n###' % (cmd,nstkurl,) ) 245 | 246 | ### Spawn 'gunzip|tar' subproces 247 | 248 | process = subprocess.Popen( cmd, shell=True, stdin=subprocess.PIPE ) 249 | 250 | ### Read 10k compressed characters => every 100 reads will be ~1MB 251 | ### Since cspice.tar.Z ranges from 20MB to 36MB, there will be 252 | ### less than 4000 reads. 253 | 254 | n = 4000 255 | 256 | ### Read 10k compressed characters at a time and pass them to 257 | ### subprocess (gunzip|tar) 258 | 259 | zs = zurl.read(10240) 260 | while len(zs)>0: 261 | process.stdin.write( zs ) 262 | 263 | if (n%100)==0: 264 | sys.stderr.write( ' %d' % (n/100,) ) 265 | sys.stderr.flush() 266 | n -= 1 267 | zs = zurl.read(10240) 268 | 269 | print( '### Done' ) 270 | 271 | zurl.close() 272 | 273 | ######################################################################## 274 | if __name__=="__main__": 275 | """ 276 | Usage: python getnaiftoolkit.py [extract [topdir=subdir/]] 277 | """ 278 | main(sys.argv[1:]) 279 | -------------------------------------------------------------------------------- /mkwrapper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Generate a SPICE python wrapper 4 | # 5 | # Run this command by providing the location of the unpacked toolkit 6 | # directory: 7 | # 8 | # mkwrapper.py /path/to/cspice/toolkit 9 | # 10 | # Author: Roberto Aguilar, roberto.c.aguilar@jpl.nasa.gov 11 | # 12 | # Released under the BSD license, see LICENSE for details 13 | # 14 | # $Id$ 15 | 16 | import os, sys 17 | from cStringIO import StringIO 18 | 19 | # This is a parameter class that is used to hold all the information about a 20 | # parameter. Initially there is nothing in it. The object is populated with 21 | # arbritrary information as the items are needed. This is controlled using 22 | # the setattr and getattr functions in the class 23 | class Container(dict): 24 | def __getattr__(self, key): 25 | val = self.get(key, None) 26 | 27 | return val 28 | 29 | def __setattr__(self, key, val): 30 | self[key] = val 31 | 32 | 33 | # look for these types of functions in the input stream in order to 34 | # generate the wrappers. 35 | function_types = ('ConstSpiceChar', 'SpiceBoolean', 'SpiceChar', 'SpiceDouble', 'SpiceInt', 'void') 36 | 37 | RESERVED_NAMES = ('free',) 38 | 39 | # Reasons for excluding the following functions 40 | # appnd*, etc. - not looked into translating a SpiceCell to a python object yet. 41 | # axisar_c - haven't written code to parse arrays 42 | # bodvar_c - deprecated 43 | # bschoc_c, etc. - how to support const void * array 44 | # ckw05_c - how to support SpiceCK05Subtype 45 | # maxd_c - variable length inputs 46 | # spkw18_c - how to support SpiceSPK18Subtype 47 | # dafgs_c - how to deal with an array that doesn't have the number of elements 48 | # dasec_c - how to handle void types in parameter list 49 | # dafgh_c - does function actually exist? I found no C file ... 50 | # ucase_c - not needed for python 51 | # gfevnt_c, gffove_c, gfocce_c, gfuds_c, uddc_c, uddf_c - how to support callbacks 52 | exclude_list = ( 53 | 'cnames', 54 | 55 | 'appndc_c', 'appndd_c', 'appndi_c', 'zzsynccl_c', 56 | 57 | 'bodvar_c', 58 | 59 | 'bschoc_c', 'bsrchc_c', 'dafac_c', 'dafec_c', 'dasac_c', 'ekacec_c', 60 | 'ekaclc_c', 'ekbseg_c', 'zzgetcml_c', 'ekifld_c', 'ekucec_c', 'esrchc_c', 61 | 'getelm_c', 'isrchc_c', 'kxtrct_c', 'lmpool_c', 'lstlec_c', 'lstltc_c', 62 | 'mequg_c', 'mtxmg_c', 'mtxvg_c', 'mxmg_c', 'mtmtg_c', 'mxmtg_c', 'mxvg_c', 63 | 'orderc_c', 'pcpool_c', 'swpool_c', 'vtmvg_c', 'xposeg_c', 64 | 65 | 'ckw05_c', 66 | 67 | 'maxd_c', 'maxi_c', 'mind_c', 'mini_c', 68 | 69 | 'spkw18_c', 70 | 71 | 'dafgs_c', 'dafps_c', 'dafus_c', 'getfov_c', 72 | 'ckw01_c', 'ckw02_c', 'ckw03_c', 'spk14a_c', 'spkw02_c', 'spkw03_c', 73 | 'spkw05_c', 'spkw08_c', 'spkw09_c', 'spkw10_c', 'spkw12_c', 'spkw13_c', 74 | 75 | 'dasec_c', 'ekpsel_c', 'ekrcec_c', 'gcpool_c', 'gnpool_c', 76 | 77 | 'dafgh_c', 'prefix_c', 78 | 79 | 'lcase_c', 'ucase_c', 'getcml_c', 'lparse_c', 'lparsm_c', 'prompt_c', 80 | 'putcml_c', 'reordc_c', 'shellc_c', 'sumad_c', 'sumai_c', 81 | 82 | 'gfevnt_c', 'gffove_c', 'gfocce_c', 'gfuds_c', 'uddc_c', 'uddf_c', 83 | ) 84 | 85 | module_defs = [] 86 | cspice_src = None 87 | 88 | DEBUG = 0 # set it on when string is the right one 89 | 90 | INPUT_TYPE = 0 91 | OUTPUT_TYPE = 1 92 | 93 | def debug(string): 94 | if DEBUG: sys.stderr.write("%s\n" % str(string)) 95 | 96 | def determine_py_type(param_obj): 97 | type = param_obj.type 98 | 99 | param_obj.py_string = '' 100 | param_obj.spice_obj = None 101 | 102 | # functions to get a python object or spice object, respectively, for the 103 | # given variable 104 | param_obj.get_py_fn = None 105 | param_obj.get_spice_fn = None 106 | 107 | # determine the type of python variable this would be 108 | if type in ('SpiceChar', 'ConstSpiceChar', 'SpiceChar'): 109 | param_obj.py_string = 's' 110 | elif type in ('ConstSpiceDouble', 'SpiceDouble'): 111 | param_obj.py_string = 'd' 112 | elif type in ('ConstSpiceBoolean', 'SpiceBoolean'): 113 | param_obj.py_string = 'i' 114 | elif type in ('ConstSpiceInt', 'SpiceInt'): 115 | # put a long for a spice int since they are long integers 116 | param_obj.py_string = 'l' 117 | elif type == 'SpiceCell': 118 | param_obj.py_string = 'O' 119 | param_obj.spice_obj = 'Cell' 120 | param_obj.get_py_fn = 'get_py_cell' 121 | param_obj.get_spice_fn = 'get_spice_cell' 122 | elif type in ('ConstSpiceEllipse', 'SpiceEllipse'): 123 | param_obj.py_string = 'O' 124 | param_obj.spice_obj = 'Ellipse' 125 | param_obj.get_py_fn = 'get_py_ellipse' 126 | param_obj.get_spice_fn = 'get_spice_ellipse' 127 | elif type == 'SpiceEKAttDsc': 128 | param_obj.py_string = 'O' 129 | param_obj.spice_obj = 'EkAttDsc' 130 | param_obj.get_py_fn = 'get_py_ekattdsc' 131 | param_obj.get_spice_fn = 'get_spice_ekattdsc' 132 | elif type == 'SpiceEKSegSum': 133 | param_obj.py_string = 'O' 134 | param_obj.spice_obj = 'EkSegSum' 135 | param_obj.get_py_fn = 'get_py_eksegsum' 136 | param_obj.get_spice_fn = 'get_spice_eksegsum' 137 | elif type in ('ConstSpicePlane', 'SpicePlane'): 138 | param_obj.py_string = 'O' 139 | param_obj.spice_obj = 'Plane' 140 | param_obj.get_py_fn = 'get_py_plane' 141 | param_obj.get_spice_fn = 'get_spice_plane' 142 | elif type in ('void'): 143 | param_obj.py_string = '' 144 | else: 145 | sys.stderr.write('Warning: Unknown type: %s\n' % type) 146 | 147 | def get_doc(function_name): 148 | doc = StringIO() 149 | 150 | # the sections of documentation we want 151 | sections = [ 152 | '-Abstract', 153 | '-Brief_I/O', 154 | '-Detailed_Input', 155 | '-Detailed_Output', 156 | ] 157 | 158 | src_file = "%s/%s.c" % (cspice_src, function_name) 159 | 160 | if os.path.exists(src_file): 161 | f = open(src_file, 'r') 162 | 163 | # loop for going through the entire source file 164 | while 1: 165 | input = f.readline() 166 | input_len = len(input) 167 | 168 | if not input: break 169 | 170 | input = input.rstrip() 171 | 172 | # skip blank lines 173 | if input == "": continue 174 | 175 | #print "input: %s" % input 176 | 177 | split = input.split() 178 | 179 | if split[0] in sections: 180 | while 1: 181 | if not input: 182 | doc.write('\\n') 183 | else: 184 | doc.write('%s\\n' % input.replace('\\', '\\\\').replace('"', '\\"')) 185 | 186 | input = f.readline() 187 | input_len = len(input) 188 | input = input.rstrip() 189 | 190 | if not input: 191 | continue 192 | elif input[0] == '-': 193 | f.seek(f.tell()-input_len) 194 | break 195 | 196 | # t = f.readlines() 197 | f.close() 198 | 199 | return '"%s"' % doc.getvalue() 200 | 201 | def gen_wrapper(prototype, buffer): 202 | prototype = remove_extra_spaces(prototype) 203 | manually_build_returnVal = False 204 | input_list = [] 205 | input_name_list = [] 206 | output_list = [] 207 | output_name_list = [] 208 | 209 | # keep track of whether a character string was found as an output. 210 | # if one was found, one of the inputs to the C function is the 211 | # length of the string. since python takes care of this behind 212 | # the scenes, there will not be a length input from the python 213 | # script, so pass in the size of the locally made string (most 214 | # likely STRING_LEN above). 215 | string_output_num = 0 216 | 217 | # parse up the given prototype into its fundamental pieces. this function 218 | # returns a container object with all the information parsed up. 219 | prototype_obj = parse_prototype(prototype) 220 | 221 | # check the exclude list before continuing 222 | if prototype_obj.function_name in exclude_list: return False 223 | 224 | # the string that is passed to PyArg_ParseTuple for getting the 225 | # arguments list and Py_BuildValue for returning results 226 | parse_tuple_string = "" 227 | buildvalue_string = "" 228 | 229 | # remove the _c suffix for the python function name 230 | python_function_name = prototype_obj.function_name.rsplit('_c',1)[0] 231 | 232 | # Add the C function prototype to the source code output. 233 | t = '/* %s */' % prototype 234 | prototype_comment_list = [] 235 | while len(t) > 80: 236 | count = 79 237 | while t[count-1] != ',' and count > 1: 238 | count -= 1 239 | 240 | prototype_comment_list.append(t[:count]) 241 | t = t[count:] 242 | if t: 243 | prototype_comment_list.append(t) 244 | 245 | prototype_comment = '\n'.join(prototype_comment_list) 246 | 247 | # declare the function for this wrapper 248 | buffer.write( 249 | "\n%s\nstatic PyObject * spice_%s(PyObject *self, PyObject *args)\n{" % \ 250 | (prototype_comment, python_function_name)); 251 | 252 | # split up the inputs from the outputs so that only inputs have to 253 | # be provided to the function from python and the outputs are 254 | # returned to python, e.g. et = utc2et("SOME DATE") 255 | last_item = None 256 | t_type = None 257 | for param in prototype_obj.params: 258 | #debug('') 259 | param_info = parse_param(param) 260 | if param_info is None: 261 | continue 262 | #debug("parsed param: %s" % param_info) 263 | 264 | if param_info.is_array and not param_info.is_const: 265 | t_type = OUTPUT_TYPE 266 | elif param_info.is_const or not param_info.is_pointer: 267 | t_type = INPUT_TYPE 268 | elif param_info.is_pointer: 269 | t_type = OUTPUT_TYPE 270 | else: 271 | raise Exception("I don't know if %s is an input or output" % param) 272 | 273 | # tack the parameter type onto the param_info tuple 274 | param_info.param_type = t_type 275 | 276 | # if the last param was counted as an output, but this param 277 | # is an input, bring the last entered item to this list 278 | # because it was miscategorized 279 | # 280 | # TODO: This is a HUGE hack and would be nice to find an alternate 281 | # way of deciding this). 282 | 283 | if last_item is not None and \ 284 | t_type == INPUT_TYPE and last_item.param_type == OUTPUT_TYPE: 285 | output_list.remove(last_item) 286 | input_list.append(last_item) 287 | 288 | if t_type == INPUT_TYPE: 289 | input_list.append(param_info) 290 | else: 291 | output_list.append(param_info) 292 | 293 | last_item = param_info 294 | #debug("param after hack: %s" % param_info) 295 | 296 | #debug("") 297 | 298 | # parse the outputs 299 | if output_list: 300 | buffer.write("\n /* variables for outputs */") 301 | 302 | #print 'function_name: %s' % function_name 303 | 304 | # the bodv* functions pass in 'maxn', which is the number of elements in 305 | # the output array 'values'. detect this case and allocate memory for the 306 | # values array. 307 | # 308 | # the allocate_memory variable points to the last input variable, which 309 | # should be the variable pointing to the max number of items in the array 310 | if prototype_obj.function_name.startswith('bodv'): 311 | output_list[-1].make_pointer = True 312 | output_list[-1].allocate_memory = input_list[-1].name 313 | manually_build_returnVal = True 314 | pass 315 | 316 | for output in output_list: 317 | #print output 318 | 319 | # declare the variable 320 | buffer.write("\n %s" % output.type) 321 | 322 | if output.make_pointer: 323 | buffer.write(" *") 324 | 325 | buffer.write(" %s" % output.name) 326 | 327 | # this item may be an array pointer, so only add the brackets 328 | # if no memory allocation was done for this variable 329 | if output.is_array and not output.allocate_memory: 330 | for count in output.num_elements: 331 | try: 332 | buffer.write("[%d]" % count) 333 | except: 334 | buffer.write("[]") 335 | 336 | if output.type == 'SpiceChar' and not output.is_array: 337 | string_output_num += 1 338 | buffer.write("[STRING_LEN]") 339 | 340 | buffer.write(";") 341 | 342 | output_name_list.append(output.name) 343 | 344 | # parse the inputs 345 | if input_list: 346 | buffer.write("\n /* variables for inputs */") 347 | 348 | # in the loop below, the variables are being declared as type 349 | # 'reg_type'. this is because python was not properly parsing the 350 | # arguments passed in from Python when using SPICE variable types. 351 | py_to_c_conversions = []; 352 | 353 | for input in input_list: 354 | input_name = input.name 355 | 356 | # if a character string output was detected and this variable has the 357 | # string 'len' in it, skip adding it to the ParseTuple string. 358 | if 'len' in input_name and string_output_num > 0: 359 | buffer.write("\n %s %s = STRING_LEN;" % \ 360 | (input.reg_type, input_name) 361 | ) 362 | else: 363 | parse_tuple_string += input.py_string 364 | 365 | if input.is_pointer: 366 | pointer_string = " * " 367 | else: 368 | pointer_string = " " 369 | 370 | buffer.write("\n %s%s%s" % ( 371 | input.reg_type, pointer_string, input_name)) 372 | 373 | if input.is_array: 374 | for count in input.num_elements: 375 | buffer.write("[%s]" % count) 376 | 377 | buffer.write(";") 378 | 379 | # if this input has a get_spice_fn function associated with it, 380 | # declare a variable for the conversion 381 | if input.get_spice_fn: 382 | input_name = "py_%s" % input_name 383 | buffer.write("\n PyObject * %s = NULL;" % input_name) 384 | py_to_c_conversions.append("%s = %s(%s);" % (input.name, input.get_spice_fn, input_name)) 385 | 386 | # if this is an array, put in the right amount of elements 387 | # into the ParseTuple parameter list (one per element). 388 | # Also, the list coming in can be 1D, 2D, or 3D. 389 | if input.is_array: 390 | input_name_list += get_array_sizes( 391 | input.num_elements, input_name) 392 | else: 393 | input_name_list.append(input_name) 394 | 395 | # other variables needed below 396 | buffer.write('\n\n char failed = 0;') 397 | 398 | if not manually_build_returnVal and output_list: 399 | buffer.write('\n char buildvalue_string[STRING_LEN] = "";') 400 | 401 | # configure the input string list for parsing the args tuple 402 | input_list_string = "&" + ", &".join(input_name_list) 403 | 404 | # if the function type is not void, declare a variable for the 405 | # result. 406 | if prototype_obj.type != "void": 407 | buffer.write("\n\n /* variable for result */") 408 | 409 | if prototype_obj.is_pointer: 410 | t_pointer = " * " 411 | else: 412 | t_pointer = " " 413 | 414 | buffer.write("\n %s%sresult;" % (prototype_obj.type, t_pointer)) 415 | 416 | buffer.write("\n") 417 | 418 | # generate the PyArg_ParseTuple call if there were any inputs to 419 | # this function 420 | if input_name_list: 421 | buffer.write( 422 | ('\n PYSPICE_CHECK_RETURN_STATUS(' + 423 | 'PyArg_ParseTuple(args, "%s", %s));') % \ 424 | (parse_tuple_string, input_list_string)) 425 | 426 | # if there are any Python -> C conversions that need to occur, add them here. 427 | if py_to_c_conversions: 428 | buffer.write('\n %s\n' % '\n'.join(py_to_c_conversions)) 429 | 430 | for output in output_list: 431 | # see if memory needs to be allocated for this variable 432 | if output.allocate_memory: 433 | buffer.write("\n\n %s = malloc(sizeof(%s) * %s);" % \ 434 | (output.name, output.type, output.allocate_memory)) 435 | 436 | # build the input name list for calling the C function 437 | input_name_list = [] 438 | for input in input_list: 439 | input_name_list.append(input.name) 440 | 441 | # combine the input name list and the output name list for the C 442 | # function call 443 | if input_list: 444 | input_list_string = ", ".join(input_name_list) 445 | else: 446 | input_list_string = "" 447 | 448 | # build the output name list for calling the C function 449 | t_list = [] 450 | for output in output_list: 451 | if any([output.is_array, 452 | output.type == "SpiceChar", 453 | output.allocate_memory]): 454 | t_list.append(output.name) 455 | else: 456 | t_list.append("&" + output.name) 457 | 458 | if t_list: 459 | output_list_string = ', '.join(t_list) 460 | else: 461 | output_list_string = '' 462 | 463 | #debug("output list: %s" % output_list_string) 464 | 465 | if input_list_string and output_list_string: 466 | param_list_string = "%s, %s" % (input_list_string, output_list_string) 467 | elif input_list_string: 468 | param_list_string = input_list_string 469 | else: 470 | param_list_string = output_list_string 471 | 472 | # debug(param_list_string) 473 | 474 | # Call the C function 475 | if prototype_obj.type != "void": 476 | buffer.write("\n result = %s(%s);" % (prototype_obj.function_name, param_list_string)) 477 | else: 478 | buffer.write("\n %s(%s);" % (prototype_obj.function_name, param_list_string)) 479 | 480 | # run the macro to check to see if an exception was raised. once the 481 | # check is made, see if the failed boolean was set. this is an indication 482 | # that the function should free any allocated memory and return NULL. 483 | buffer.write("\n\n PYSPICE_CHECK_FAILED;\n") 484 | 485 | buffer.write('\n if(failed) {') 486 | 487 | for output in output_list: 488 | if output.allocate_memory: 489 | buffer.write('\n free(%s);' % output.name) 490 | 491 | buffer.write('\n return NULL;') 492 | buffer.write('\n }\n') 493 | 494 | # loop through the output to build the value strings for each output if 495 | # the return value is not being built up manually 496 | if not manually_build_returnVal: 497 | if output_list: 498 | buffer.write('\n /* put together the output buildvalue string */') 499 | 500 | for output in output_list: 501 | if output.allocate_memory: 502 | buffer.write( 503 | ('\n make_buildvalue_tuple(buildvalue_string, ' + 504 | '"%s", %s);') % (output.py_string, output.name) 505 | ) 506 | elif output.name != 'found': 507 | buffer.write( 508 | '\n strcat(buildvalue_string, "%s");' % output.py_string 509 | ) 510 | 511 | buffer.write('\n') 512 | 513 | # If the called function is a void, return PyNone, or else figure out what 514 | # to return. 515 | if output_list: 516 | # output_list_string = ', '.join(output_list) 517 | #debug('output_list: '.format(output_list)) 518 | if manually_build_returnVal: 519 | #debug('in manual returnVal build') 520 | make_manual_returnVal(buffer, output_list) 521 | else: 522 | #debug('in automatic returnVal build') 523 | make_automatic_returnVal(buffer, output_list) 524 | elif prototype_obj.type == "void": 525 | buffer.write("\n Py_INCREF(Py_None);") 526 | buffer.write("\n return Py_None;") 527 | elif prototype_obj.type == "SpiceBoolean": 528 | buffer.write("\n if(result) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; }") 529 | elif prototype_obj.type in ("ConstSpiceChar", "SpiceDouble", "SpiceInt"): 530 | buffer.write('\n return Py_BuildValue("%s", result);' % prototype_obj.py_string) 531 | else: 532 | pass # for now; TODO: figure out what to do 533 | 534 | buffer.write("\n}"); 535 | 536 | # dig out the function name from the source file 537 | doc = get_doc(prototype_obj.function_name) 538 | 539 | if not doc: 540 | buffer.write('\nPyDoc_STRVAR(%s_doc, "");\n' % python_function_name) 541 | else: 542 | buffer.write('\nPyDoc_STRVAR(%s_doc, %s);\n' % (python_function_name, doc)) 543 | 544 | # add this functions definition to the module_defs list 545 | module_defs.append('{"%s", spice_%s, METH_VARARGS, %s_doc},' % \ 546 | (python_function_name, python_function_name, python_function_name)) 547 | 548 | return buffer.getvalue() 549 | 550 | def get_array_sizes(list, name): 551 | """ 552 | Expand the elements of an array for 1, 2, and 3D arrays 553 | """ 554 | t_list = [] 555 | 556 | if len(list) == 1: 557 | for t in range(0, list[0]): 558 | t_list.append(name + "[%s]" % t) 559 | elif len(list) == 2: 560 | for t1 in range(0, list[0]): 561 | for t2 in range(0, list[1]): 562 | t_list.append(name + "[%s][%s]" % (t1, t2)) 563 | elif len(list) == 3: 564 | for t1 in range(0, list[0]): 565 | for t2 in range(0, list[1]): 566 | for t3 in range(0, list[2]): 567 | t_list.append(name + "[%s][%s][%s]" % (t1, t2, t3)) 568 | 569 | return t_list 570 | 571 | def make_automatic_returnVal(buffer, output_list): 572 | """ 573 | The outputs parameters and their dimensions are defined so this function 574 | can be used. 575 | """ 576 | 577 | # put together the outputs 578 | t_list = [] 579 | buildvalue_string = '' 580 | 581 | # Check_found is used to indicate whether a found variable was 582 | # passed along with other output variables. check_found is set to 583 | # True if the found variable indicating that the additional 584 | # outputs are valid. This way, if nothing was found, the function 585 | # returns None instead of the output variables requested. 586 | check_found = False 587 | 588 | for output in output_list: 589 | # if the length of the output list is only 1 and it's found, 590 | # return true or false, otherwise, if found is false return None. 591 | if output.name == "found": 592 | if len(output_list) == 1: 593 | buffer.write( 594 | '\n if(found) { Py_RETURN_TRUE; } ' + 595 | 'else { Py_RETURN_FALSE; }' 596 | ) 597 | else: 598 | check_found = True 599 | continue 600 | 601 | # If the output is an array, expand out all the elements in order 602 | # to build a python tuple out of them (This seems sub-optimal, I 603 | # have to read some more python C documentation). 604 | if output.is_array: 605 | t_list += get_array_sizes(output.num_elements, output.name) 606 | elif(output.get_py_fn): 607 | t_list.append('%s(&%s)' % (output.get_py_fn, output.name)) 608 | else: 609 | t_list.append(output.name) 610 | 611 | buildvalue_string += output.py_string 612 | 613 | output_list_string = ", ".join(t_list) 614 | 615 | if output_list_string: 616 | if check_found: 617 | buffer.write( 618 | '\n if(!found) {\n return Py_None;\n } else {\n ') 619 | else: 620 | buffer.write('\n ') 621 | 622 | buffer.write( 623 | 'PyObject *returnVal = Py_BuildValue(buildvalue_string, %s);' % \ 624 | (output_list_string)) 625 | 626 | for output in output_list: 627 | if output.allocate_memory: 628 | buffer.write('\n free(%s);' % output.name) 629 | 630 | buffer.write('\n return returnVal;'); 631 | 632 | if check_found: 633 | buffer.write('\n }\n') 634 | 635 | def make_manual_returnVal(buffer, output_list): 636 | """ 637 | This returnVal function is used when the output dimensions are dynamic. 638 | For instance, a double * and an integer specifying the number of elements 639 | in that array are passed in. In order to properly build the return tuple, 640 | this function can be used. 641 | """ 642 | 643 | buffer.write('\n int i = 0;') 644 | buffer.write('\n PyObject *t = NULL;') 645 | buffer.write( 646 | '\n PyObject *returnVal = PyTuple_New(%d);' % len(output_list)) 647 | 648 | count = 0; 649 | for count in range(len(output_list)): 650 | output = output_list[count] 651 | 652 | if output.allocate_memory: 653 | buffer.write('\n t = PyTuple_New(%s);' % output_list[0].name) 654 | buffer.write( 655 | '\n for(i = 0; i < %s; ++ i) {' % output_list[0].name 656 | ) 657 | buffer.write( 658 | '\n PyTuple_SET_ITEM(t, i, Py_BuildValue("%s", %s[i]));' % \ 659 | (output.py_string, output.name) 660 | ) 661 | buffer.write('\n }') 662 | buffer.write('\n PyTuple_SET_ITEM(returnVal, %d, t);' % count) 663 | else: 664 | buffer.write( 665 | '\n PyTuple_SET_ITEM(returnVal, %d, Py_BuildValue("%s", %s));' % \ 666 | (count, output.py_string, output.name) 667 | ) 668 | 669 | buffer.write('\n return returnVal;'); 670 | 671 | def get_type(type): 672 | reg_type = type 673 | 674 | # determine the type of python variable this would be 675 | if type in ('SpiceChar', 'ConstSpiceChar', 'SpiceChar'): 676 | reg_type = 'char' 677 | elif type in ('ConstSpiceDouble', 'SpiceDouble'): 678 | reg_type = 'double' 679 | elif type in ('ConstSpiceBoolean', 'SpiceBoolean'): 680 | reg_type = 'char' 681 | elif type in ('ConstSpiceInt', 'SpiceInt'): 682 | # put a long for a spice int since they are long integers 683 | reg_type = 'long' 684 | 685 | return reg_type 686 | 687 | def get_tuple_py_string(param_obj, curr_depth=0): 688 | """ 689 | Put together the tuple string based on the number of elements in 690 | the given elements list. if this was a one demensional array, the 691 | list would simply be, for example [3], where 3 is the number of 692 | elements in the array. A 3x3 2D array would be [3, 3] a 3x3x3 3D 693 | array is: [3, 3, 3]. The output for these examples would be: 694 | 695 | (xxx) 696 | ((xxx)(xxx)(xxx)) 697 | (((xxx)(xxx)(xxx))((xxx)(xxx)(xxx))((xxx)(xxx)(xxx))) 698 | """ 699 | 700 | list = param_obj.num_elements 701 | char = param_obj.py_string 702 | 703 | t = "(" 704 | 705 | for i in range(0, list[curr_depth]): 706 | if curr_depth < (len(list) - 1): 707 | t += get_tuple_py_string(param_obj, (curr_depth+1)) 708 | else: 709 | t += char 710 | 711 | t += ")" 712 | 713 | return t 714 | 715 | def fixNameCollision(name): 716 | if name in RESERVED_NAMES: 717 | return name + '_' 718 | else: 719 | return name 720 | 721 | def parse_param(param): 722 | """ 723 | Take the given parameter and break it up into the type of 724 | variable, the variable name and whether it's a pointer. 725 | """ 726 | 727 | param_obj = Container() 728 | 729 | param = remove_extra_spaces(param) 730 | param_split = param.split(" ") 731 | 732 | param_obj.original = param 733 | 734 | #debug('param: %s' % param) 735 | #debug('param_split: %s' % str(param_split)) 736 | 737 | index = 0 738 | 739 | type = param_split[index] 740 | index += 1 741 | 742 | if type == 'const': 743 | param_obj.is_const = True 744 | type = param_split[index] 745 | index += 1 746 | else: 747 | param_obj.is_const = False 748 | 749 | param_obj.type = type 750 | param_obj.reg_type = get_type(type) 751 | 752 | try: 753 | name = param_split[index] 754 | index += 1 755 | 756 | # check to see if the second element in the split list is a 757 | # pointer, if so, set the is_pointer var to True and set the name 758 | # to the next element in the list 759 | if name == "*": 760 | param_obj.is_pointer = True 761 | name = param_split[index] 762 | index += 1 763 | else: 764 | param_obj.is_pointer = False 765 | 766 | # now check to see if the pointer is stuck on the variable 767 | if name[0] == "*": 768 | param_obj.is_pointer = True 769 | name = name[1:] 770 | 771 | # check for const in the type name 772 | if type.startswith('Const'): 773 | param_obj.is_const = True 774 | 775 | # look for brackets in the param string. this can be a 1D 776 | # array, e.g. int x[3] or a multidimensional array, e.g. int 777 | # x[4][4] 778 | start_find = 0 779 | param_obj.num_elements = [] 780 | 781 | while 1: 782 | open_bracket_pos = param.find('[', start_find) 783 | start_find = open_bracket_pos + 1 784 | 785 | #debug('open_bracket_pos: %s' % open_bracket_pos) 786 | 787 | # if no open bracket was found, break out of the loop 788 | if open_bracket_pos == -1: 789 | break 790 | close_bracket_pos = param.find(']', start_find) 791 | 792 | #debug('close_bracket_pos: %s' % close_bracket_pos) 793 | 794 | # try to convert the number of the elements into an 795 | # integer. this may fail if the brackets are empty. If 796 | # they are empty, don't fail, or else raise the exception. 797 | t = param[open_bracket_pos+1:close_bracket_pos] 798 | 799 | #debug('t: %s' % t) 800 | 801 | try: 802 | param_obj.num_elements.append(int(t)) 803 | except Exception, msg: 804 | if t: 805 | raise msg 806 | param_obj.num_elements.append('') 807 | 808 | #debug('num_elements: %s' % str(param_obj.num_elements)) 809 | 810 | #debug('num_elements (out of loop): %s' % param_obj.num_elements) 811 | 812 | # check if the bracket was stuck on the variable and 813 | # remove it 814 | name_bracket_pos = name.find('[') 815 | if name_bracket_pos > -1: 816 | name = name[0:name_bracket_pos] 817 | 818 | param_obj.name = fixNameCollision(name) 819 | param_obj.is_array = len(param_obj.num_elements) 820 | determine_py_type(param_obj) 821 | 822 | #debug('type: %s' % param_obj.type) 823 | #debug('is_const: %s' % param_obj.is_const) 824 | #debug('name: %s' % param_obj.name) 825 | #debug('is_pointer: %s' % param_obj.is_pointer) 826 | #debug('is_array: %s' % str(param_obj.is_array)) 827 | #debug('py_string: %s' % param_obj.py_string) 828 | 829 | if param_obj.is_array: 830 | param_obj.py_string = get_tuple_py_string(param_obj) 831 | 832 | return param_obj 833 | except Exception, msg: 834 | if type == 'void': 835 | return None 836 | else: 837 | raise msg 838 | 839 | def parse_prototype(prototype): 840 | """ 841 | Take the given function prototype and break it up into the type of 842 | function, the function name, whether it is a pointer and the 843 | parameters it takes. 844 | """ 845 | 846 | prototype = prototype.strip() 847 | prototype_split = prototype.split(" ") 848 | type = prototype_split[0] 849 | prototype_obj = Container() 850 | 851 | if prototype_split[1] == "*": 852 | is_pointer = True 853 | function_name = prototype_split[2] 854 | params = " ".join(prototype_split[3:]) 855 | else: 856 | is_pointer = False 857 | function_name = prototype_split[1] 858 | params = " ".join(prototype_split[2:]) 859 | 860 | # if the function's open paren is stuck to the function name, remove it 861 | if function_name.endswith("("): 862 | function_name = function_name.rstrip('(') 863 | params = "(" + params 864 | 865 | # if the pointer was stuck to the function name, remove it 866 | if function_name.startswith('*'): 867 | is_pointer = True 868 | function_name = function_name.lstrip('*') 869 | 870 | try: 871 | params = params[params.index("(")+1:params.index(")")].strip().split(",") 872 | 873 | #debug("type: %s, function: %s, is_pointer: %s, params: %s" % \ 874 | # (type, function_name, is_pointer, params)) 875 | except: 876 | #debug("unable to parse params for function %s" % function_name) 877 | debug(function_name[-1]) 878 | 879 | prototype_obj.type = type 880 | prototype_obj.is_pointer = is_pointer 881 | prototype_obj.function_name = function_name 882 | prototype_obj.params = params 883 | determine_py_type(prototype_obj) 884 | 885 | #debug('function_name: %s, type: %s, py_string: %s\n' % \ 886 | # (prototype_obj.function_name, 887 | # prototype_obj.type, 888 | # prototype_obj.py_string)) 889 | 890 | return prototype_obj 891 | 892 | def remove_extra_spaces(string): 893 | """ 894 | strip out extra spaces in the given string 895 | """ 896 | 897 | string = string.strip() 898 | 899 | last_char = "" 900 | 901 | i = 0 902 | while i < len(string): 903 | s = string[i] 904 | 905 | if s == " " and last_char == " ": 906 | string = string[0:i] + string[i+1:] 907 | else: 908 | i += 1 909 | 910 | last_char = s 911 | 912 | return string 913 | 914 | def run_command(command, writer): 915 | """ 916 | run_command(command, writer) 917 | 918 | Run the given command and write the output to the given writer object 919 | 920 | command - a string representing the command to run 921 | writer - any object that has a write(string) function. 922 | 923 | The function returns the given command's exit status. 924 | 925 | Here's a typical usage scenario: 926 | 927 | # import everything from this module 928 | from command import * 929 | 930 | # create a StringIO object 931 | s = StringIO() 932 | 933 | # run the desired command 934 | status = run_command('ls', s) 935 | 936 | # print the result 937 | print s.getvalue() 938 | 939 | # print the command's exit status 940 | print 'exit status: %d' % (status,) 941 | """ 942 | 943 | command_handle = os.popen(command, 'r') 944 | 945 | output = command_handle.read() 946 | writer.write(output) 947 | 948 | return command_handle.close() 949 | 950 | def main(cspice_toolkit): 951 | global cspice_src 952 | 953 | parsing_prototype = False 954 | curr_prototype = '' 955 | module_methods = StringIO() 956 | buffer = StringIO() 957 | 958 | cspice_header = os.path.join(cspice_toolkit, 'include', 'SpiceUsr.h') 959 | cspice_src = os.path.join(cspice_toolkit, 'src', 'cspice') 960 | 961 | if not os.path.exists(cspice_header): 962 | sys.exit('Error: Unable to find %s' % cspice_header) 963 | 964 | if not os.path.exists(cspice_src): 965 | sys.exit('Error: Unable to find %s' % cspice_header) 966 | 967 | # preprocess the header file 968 | output = StringIO() 969 | run_command('gcc -E %s' % cspice_header, output) 970 | output.reset() 971 | 972 | used_prototypes = 0 973 | total_prototypes = 0; 974 | line_no = 0 975 | while 1: 976 | input = output.readline() 977 | line_no +=1 978 | # #debug('line: {1}, input: {0}'.format(input,line_no)) 979 | if input == "": 980 | break 981 | input = input.strip() 982 | 983 | if input == "": 984 | continue 985 | # if parsing still false and line does not have opening bracket 986 | elif not parsing_prototype and "(" not in input: 987 | continue 988 | if parsing_prototype: 989 | # #debug("parse_adding: %s" % input) 990 | 991 | curr_prototype += input 992 | 993 | # #debug("curr_prototype now: %s" % curr_prototype) 994 | 995 | # if the last character is a semi-colon, the prototype is 996 | # complete, pass it along to the wrapper generator and clear 997 | # out the curr_prototype variable 998 | if input.endswith(";"): 999 | parsing_prototype = False 1000 | 1001 | # gen_wrapper can return False, then don't count it as used 1002 | #debug('') 1003 | #debug('%s\n' % curr_prototype) 1004 | if gen_wrapper(curr_prototype, buffer): 1005 | used_prototypes += 1 1006 | total_prototypes += 1 1007 | 1008 | curr_prototype = "" 1009 | pass 1010 | else: 1011 | first_word = input[0:input.index(" ")] 1012 | # #debug('first_word: {0}'.format(first_word)) 1013 | if first_word in function_types: 1014 | # #debug("adding %s" % input) 1015 | 1016 | curr_prototype += input 1017 | 1018 | # #debug("curr_prototype now: %s" % curr_prototype) 1019 | else: 1020 | continue 1021 | if input.endswith(";"): 1022 | # #debug("prototype to be wrapped: {0}".format(curr_prototype)) 1023 | gen_wrapper(curr_prototype, buffer) 1024 | curr_prototype = "" 1025 | else: 1026 | parsing_prototype = True 1027 | 1028 | sys.stderr.write("prototypes used: %d, total: %d\n" % (used_prototypes, total_prototypes)) 1029 | # put together the methods array 1030 | for module_def in module_defs: 1031 | module_methods.write("\n %s" % module_def) 1032 | 1033 | # print out necessary boilerplate stuff 1034 | return """\ 1035 | /* 1036 | THIS IS AUTOMATICALLY GENERATED CODE. IF THERE IS AN ERROR, PLEASE 1037 | MAKE ANY NECESSARY CHANGES IN THE PYTHON SCRIPT NAMED mkwrapper.py. 1038 | 1039 | THIS CODE HAS NOT BEEN THOROUGHLY TESTED, USE AT YOUR OWN RISK, THE 1040 | AUTHOR(S) IS/ARE NOT RESPONSIBLE IF YOUR CRAFT GOES DOWN, BLAH BLAH 1041 | BLAH. SEE FILE "LICENSE" FOR MORE INFO. 1042 | */ 1043 | 1044 | #include "pyspice.h" 1045 | 1046 | PyObject *SpiceException; 1047 | 1048 | %s 1049 | PyMethodDef methods[] = { 1050 | %s 1051 | {NULL, NULL}, 1052 | }; 1053 | 1054 | void init_spice(PyObject *self) 1055 | { 1056 | PyObject *m = NULL; 1057 | 1058 | m = Py_InitModule("_spice", methods); 1059 | 1060 | /* Don't allow an exception to stop execution */ 1061 | erract_c("SET", 0, "RETURN"); 1062 | errdev_c("SET", 0, "NULL"); 1063 | 1064 | SpiceException = \ 1065 | PyErr_NewException("_spice.SpiceException", PyExc_Exception, NULL); 1066 | Py_INCREF(SpiceException); 1067 | 1068 | PyModule_AddObject(m, "SpiceException", SpiceException); 1069 | }""" % (buffer.getvalue(), module_methods.getvalue()) 1070 | 1071 | if __name__ == '__main__': 1072 | if sys.argv: 1073 | cspice_toolkit = sys.argv[1] 1074 | else: 1075 | sys.exit('Please provide the path to the unpacked cspice toolkit directory') 1076 | 1077 | print main(cspice_toolkit) 1078 | -------------------------------------------------------------------------------- /pyspice.c: -------------------------------------------------------------------------------- 1 | /** 2 | * PySPICE implementation file 3 | * 4 | * This file contains some helper functions for going between C and Python 5 | * 6 | * Author: Roberto Aguilar, roberto.c.aguilar@jpl.nasa.gov 7 | * 8 | * Released under the BSD license, see LICENSE for details 9 | * 10 | * $Id$ 11 | */ 12 | #include "pyspice.h" 13 | 14 | void make_buildvalue_tuple(char *buf, const char *type, const int count) 15 | { 16 | int i = 0; 17 | 18 | strcat(buf, "("); 19 | 20 | for(i = 0; i < count; ++ i) { 21 | strcat(buf, type); 22 | } 23 | 24 | strcat(buf, ")"); 25 | } 26 | 27 | static PyObject * get_double_list(double *array, const int count) 28 | { 29 | int i = 0; 30 | /* set the center */ 31 | PyObject *list = PyList_New(count); 32 | 33 | if(list) { 34 | for(i = 0; i < count; ++ i) { 35 | PyObject *d = PyFloat_FromDouble(array[i]); 36 | PyList_SET_ITEM(list, i, d); 37 | } 38 | } 39 | 40 | return list; 41 | } 42 | 43 | /** 44 | * Create a Python Ellipse object from a SpiceEllipse object 45 | */ 46 | PyObject * get_py_ellipse(SpiceEllipse *spice_obj) 47 | { 48 | PyObject *py_obj = NULL; 49 | PyObject *module = PyImport_ImportModule("spice"); 50 | 51 | if(module) { 52 | PyObject *py_cls = PyObject_GetAttrString(module, "Ellipse"); 53 | 54 | if(py_cls) { 55 | py_obj = PyObject_CallObject(py_cls, NULL); 56 | 57 | if(py_obj) { 58 | PyObject_SetAttrString(py_obj, "center", get_double_list(spice_obj->center, 3)); 59 | PyObject_SetAttrString(py_obj, "semi_major", get_double_list(spice_obj->semiMajor, 3)); 60 | PyObject_SetAttrString(py_obj, "semi_minor", get_double_list(spice_obj->semiMinor, 3)); 61 | } 62 | } 63 | } 64 | 65 | return py_obj; 66 | } 67 | 68 | PyObject * get_py_cell(SpiceCell *cell) 69 | { 70 | PyObject *py_obj = NULL; 71 | return py_obj; 72 | } 73 | 74 | PyObject * get_py_ekattdsc(SpiceEKAttDsc *spice_obj) 75 | { 76 | PyObject *py_obj = NULL; 77 | return py_obj; 78 | } 79 | 80 | PyObject * get_py_eksegsum(SpiceEKSegSum *spice_obj) 81 | { 82 | PyObject *py_obj = NULL; 83 | return py_obj; 84 | } 85 | 86 | PyObject * get_py_plane(SpicePlane *spice_obj) 87 | { 88 | PyObject *py_obj = NULL; 89 | PyObject *module = PyImport_ImportModule("spice"); 90 | 91 | if(module) { 92 | PyObject *py_cls = PyObject_GetAttrString(module, "Plane"); 93 | 94 | if(py_cls) { 95 | py_obj = PyObject_CallObject(py_cls, NULL); 96 | 97 | if(py_obj) { 98 | PyObject_SetAttrString(py_obj, "constant", PyFloat_FromDouble(spice_obj->constant)); 99 | PyObject_SetAttrString(py_obj, "normal", get_double_list(spice_obj->normal, 3)); 100 | } 101 | } 102 | } 103 | 104 | return py_obj; 105 | } 106 | 107 | 108 | SpiceCell * get_spice_cell(PyObject *py_obj) 109 | { 110 | SpiceCell *spice_obj = NULL; 111 | return spice_obj; 112 | } 113 | 114 | SpiceEKAttDsc * get_spice_ekattdsc(PyObject *py_obj) 115 | { 116 | SpiceEKAttDsc *spice_obj = NULL; 117 | return spice_obj; 118 | } 119 | 120 | SpiceEKSegSum * get_spice_eksegsum(PyObject *py_obj) 121 | { 122 | SpiceEKSegSum *spice_obj = NULL; 123 | return spice_obj; 124 | } 125 | 126 | SpicePlane * get_spice_plane(PyObject *py_obj) 127 | { 128 | SpicePlane *spice_obj = malloc(sizeof(SpicePlane)); 129 | 130 | PyObject *l = NULL, *f = NULL; 131 | 132 | /* set the constant variable in the spice_obj */ 133 | f = PyObject_GetAttrString(py_obj, "constant"); 134 | spice_obj->constant = PyFloat_AsDouble(f); 135 | 136 | /* now set each element in the normal array */ 137 | l = PyObject_GetAttrString(py_obj, "normal"); 138 | 139 | f = PyList_GetItem(l, 0); 140 | spice_obj->normal[0] = PyFloat_AsDouble(f); 141 | 142 | f = PyList_GetItem(l, 1); 143 | spice_obj->normal[1] = PyFloat_AsDouble(f); 144 | 145 | f = PyList_GetItem(l, 2); 146 | spice_obj->normal[2] = PyFloat_AsDouble(f); 147 | 148 | return spice_obj; 149 | } 150 | 151 | SpiceEllipse * get_spice_ellipse(PyObject *ellipse) 152 | { 153 | char failed = 0, *sections[3] = {"center", "semi_major", "semi_minor"}; 154 | int i, j; 155 | 156 | SpiceEllipse *spice_ellipse = malloc(sizeof(SpiceEllipse)); 157 | 158 | double *ellipse_sections[3] = {spice_ellipse->center, spice_ellipse->semiMajor, spice_ellipse->semiMinor}; 159 | 160 | for(i = 0; i < 3; ++ i) { 161 | PyObject *section = PyObject_GetAttrString(ellipse, sections[i]); 162 | 163 | if(section) { 164 | for(j = 0; j < 3; ++ j) { 165 | ellipse_sections[i][j] = PyFloat_AS_DOUBLE(PyList_GET_ITEM(section, j)); 166 | } 167 | } else { 168 | failed = 1; 169 | break; 170 | } 171 | } 172 | 173 | if(failed) { 174 | free(spice_ellipse); 175 | spice_ellipse = NULL; 176 | } 177 | 178 | return spice_ellipse; 179 | } 180 | 181 | PyObject * spice_berto(PyObject *self, PyObject *args) 182 | { 183 | PyObject *py_ellipse = NULL; 184 | 185 | PYSPICE_CHECK_RETURN_STATUS(PyArg_ParseTuple(args, "O", &py_ellipse)); 186 | 187 | SpiceEllipse *spice_ellipse = get_spice_ellipse(py_ellipse); 188 | 189 | char *sections[3] = {"center", "semi_major", "semi_minor"}; 190 | double *ellipse_sections[3] = {spice_ellipse->center, spice_ellipse->semiMajor, spice_ellipse->semiMinor}; 191 | int i = 0, j = 0; 192 | 193 | for(i = 0; i < 3; ++ i) { 194 | for(j = 0; j < 3; ++ j) { 195 | printf ("%s[%d] = %f\n", sections[i], j, ellipse_sections[i][j]); 196 | } 197 | } 198 | 199 | spice_ellipse->center[0] = 1; 200 | spice_ellipse->center[1] = 2; 201 | spice_ellipse->center[2] = 3; 202 | spice_ellipse->semiMajor[0] = 4; 203 | spice_ellipse->semiMajor[1] = 5; 204 | spice_ellipse->semiMajor[2] = 6; 205 | spice_ellipse->semiMinor[0] = 7; 206 | spice_ellipse->semiMinor[1] = 8; 207 | spice_ellipse->semiMinor[2] = 9; 208 | 209 | py_ellipse = get_py_ellipse(spice_ellipse); 210 | 211 | free(spice_ellipse); 212 | 213 | return py_ellipse; 214 | } 215 | 216 | PyObject * spice_test(PyObject *self, PyObject *args) 217 | { 218 | PyObject *py_obj = NULL; 219 | SpicePlane *plane = NULL; 220 | 221 | PYSPICE_CHECK_RETURN_STATUS(PyArg_ParseTuple(args, "O", &py_obj)); 222 | 223 | plane = get_spice_plane(py_obj); 224 | 225 | PyObject *py_obj2 = get_py_plane(plane); 226 | free(plane); 227 | 228 | return py_obj2; 229 | } 230 | -------------------------------------------------------------------------------- /pyspice.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Python SPICE wrapper header. 3 | * 4 | * This file contains some macros useful in the auto-generated spicemodule 5 | * 6 | * Author: Roberto Aguilar, roberto.c.aguilar@jpl.nasa.gov 7 | * 8 | * Released under the BSD license, see LICENSE for details 9 | */ 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #ifndef __PYSPICE_H__ 16 | #define __PYSPICE_H__ 1 17 | 18 | extern PyObject *SpiceException; 19 | 20 | #define STRING_LEN 255 21 | #define SPICE_DETAIL_LEN 1840 22 | 23 | #define PYSPICE_CHECK_RETURN_STATUS(status) { \ 24 | if(!status) { \ 25 | return NULL; \ 26 | } \ 27 | } 28 | 29 | #define PYSPICE_MAKE_EXCEPTION(detail) { \ 30 | PyErr_SetString(SpiceException, detail); \ 31 | } 32 | 33 | /* Stuff for getting the short error message 34 | #define SPICE_MESSAGE_LEN 25 35 | char *spice_msg = NULL; \ 36 | spice_msg = (char *)malloc(sizeof(char) * SPICE_MESSAGE_LEN); \ 37 | getmsg_c("short", SPICE_MESSAGE_LEN, spice_msg); \ 38 | free(spice_msg); \ 39 | */ 40 | 41 | #define PYSPICE_CHECK_FAILED { \ 42 | /* variables for exception handling */ \ 43 | char *spice_detail = NULL; \ 44 | \ 45 | /* check if the function call failed */ \ 46 | if(failed_c()) { \ 47 | spice_detail = (char *)malloc(sizeof(char) * SPICE_DETAIL_LEN); \ 48 | \ 49 | getmsg_c("long", SPICE_DETAIL_LEN, spice_detail); \ 50 | \ 51 | reset_c(); \ 52 | \ 53 | PYSPICE_MAKE_EXCEPTION(spice_detail); \ 54 | \ 55 | free(spice_detail); \ 56 | \ 57 | failed = 1; \ 58 | } \ 59 | } 60 | 61 | /* Functions defined in the implementation file */ 62 | PyObject * get_py_ellipse(SpiceEllipse *spice_obj); 63 | PyObject * get_py_cell(SpiceCell *cell); 64 | PyObject * get_py_ekattdsc(SpiceEKAttDsc *spice_obj); 65 | PyObject * get_py_eksegsum(SpiceEKSegSum *spice_obj); 66 | PyObject * get_py_plane(SpicePlane *spice_obj); 67 | SpiceCell * get_spice_cell(PyObject *py_obj); 68 | SpiceEKAttDsc * get_spice_ekattdsc(PyObject *py_obj); 69 | SpiceEKSegSum * get_spice_eksegsum(PyObject *py_obj); 70 | SpicePlane * get_spice_plane(PyObject *py_obj); 71 | SpiceEllipse * get_spice_ellipse(PyObject *ellipse); 72 | 73 | /* Some test code */ 74 | PyObject * spice_berto(PyObject *self, PyObject *args); 75 | PyObject * spice_test(PyObject *self, PyObject *args); 76 | 77 | #endif /* __PYSPICE_H__ */ 78 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Released under the BSD license, see LICENSE for details 2 | 3 | import os 4 | import sys 5 | 6 | from distutils.core import setup, Extension 7 | from shutil import copy 8 | from subprocess import PIPE, Popen 9 | 10 | APP_NAME = os.path.basename(os.getcwd()) 11 | SHARE_DIR = os.path.join('share', 'spicedoc') 12 | 13 | SPICE_MODULE_C = 'spicemodule.c' 14 | ROOT_DIR = os.path.dirname(__file__) 15 | MODULE_PATH = os.path.join(ROOT_DIR, SPICE_MODULE_C) 16 | 17 | CSPICE_SRC = os.environ.get('CSPICE_SRC', os.path.join(ROOT_DIR, 'cspice')) 18 | 19 | if not os.path.exists(CSPICE_SRC): 20 | message = ('Unable to find CSPICE source at %s. ' 21 | 'Please untar the source there or set the environment ' 22 | 'variable CSPICE_SRC') % (CSPICE_SRC,) 23 | sys.exit(message) 24 | 25 | # check to see if the lib files are named lib* 26 | remove_files = [] 27 | 28 | class LibError(Exception): pass 29 | 30 | def build_cspice(): 31 | libfile_path = os.path.join(CSPICE_SRC, 'lib', 'cspice.a') 32 | if os.path.exists(libfile_path): 33 | return 34 | 35 | curr_dir = os.getcwd() 36 | try: 37 | os.chdir(CSPICE_SRC) 38 | makeall = Popen('/bin/csh makeall.csh', shell=True) 39 | status = os.waitpid(makeall.pid, 0)[1] 40 | 41 | if status != 0: 42 | sys.stderr.write('warning: cspice build exit status: %d' % status) 43 | finally: 44 | os.chdir(curr_dir) 45 | 46 | def find_libs(): 47 | for libfile in ('cspice.a', 'csupport.a'): 48 | libfile_path = os.path.join(CSPICE_SRC, 'lib', libfile) 49 | lib_libfile_path = os.path.join(CSPICE_SRC, 'lib', 'lib' + libfile) 50 | 51 | if not os.path.exists(lib_libfile_path): 52 | if not os.path.exists(libfile_path): 53 | sys.exit('unable to find %s' % (libfile_path,)) 54 | 55 | try: 56 | copy(libfile_path, lib_libfile_path) 57 | remove_files.append(lib_libfile_path) 58 | except: 59 | raise LibError('Unable to copy %s to %s' % (libfile_path, 60 | lib_libfile_path)) 61 | 62 | def make_spice_module(): 63 | if not os.path.exists(MODULE_PATH): 64 | import mkwrapper 65 | print 'making wrapper' 66 | f = open(MODULE_PATH, 'wb') 67 | f.write(mkwrapper.main(CSPICE_SRC)) 68 | f.close() 69 | 70 | def cleanup(): 71 | for path in remove_files: 72 | os.remove(path) 73 | 74 | def set_build_paths(): 75 | if not sys.argv[1].startswith('build'): 76 | return 77 | 78 | for flag, dirname in (('-I', 'include'), ('-L', 'lib')): 79 | path = os.path.join(CSPICE_SRC, dirname) 80 | sys.argv.append('%s%s' % (flag, path)) 81 | 82 | try: 83 | build_cspice() 84 | make_spice_module() 85 | find_libs() 86 | set_build_paths() 87 | 88 | module1 = Extension( 89 | '_spice', 90 | sources = ['pyspice.c', 'spicemodule.c'], 91 | libraries = ['cspice'], 92 | ) 93 | 94 | setup( 95 | name = 'Spice', 96 | version = '1.0', 97 | description = 'Spice Wrapper Module', 98 | packages = ['spice'], 99 | ext_modules = [module1] 100 | ) 101 | finally: 102 | cleanup() 103 | -------------------------------------------------------------------------------- /spice/__init__.py: -------------------------------------------------------------------------------- 1 | # Released under the BSD license, see LICENSE for details 2 | 3 | from misc import * 4 | from objects import * 5 | -------------------------------------------------------------------------------- /spice/misc.py: -------------------------------------------------------------------------------- 1 | # Released under the BSD license, see LICENSE for details 2 | 3 | from _spice import * 4 | -------------------------------------------------------------------------------- /spice/objects.py: -------------------------------------------------------------------------------- 1 | # Released under the BSD license, see LICENSE for details 2 | 3 | class DataType(object): 4 | def __init__(self): 5 | self.SPICE_CHR = 0 6 | self.SPICE_DP = 1 7 | self.SPICE_INT = 2 8 | self.SPICE_TIME = 3 9 | self.SPICE_BOOL = 4 10 | 11 | 12 | class Cell(object): 13 | CellDataType = DataType 14 | 15 | def __init__(self, arg): 16 | self.dtype = self.CellDataType() 17 | self.length = 0 18 | self.size = 0 19 | self.card = 0 20 | self.isSet = False 21 | self.adjust = False 22 | self.init = False 23 | self.base = None # this is a void *; how to represent it? 24 | self.data = None # this is a void *; how to represent it? 25 | 26 | 27 | class Ellipse(object): 28 | """Class representing the C struct SpiceEllipse""" 29 | def __init__(self, center=None, semi_major=None, semi_minor=None): 30 | self.center = center or [0.0] * 3 31 | self.semi_major = semi_major or [0.0] * 3 32 | self.semi_minor = semi_minor or [0.0] * 3 33 | 34 | def __repr__(self): 35 | return '' % \ 36 | (self.center, self.semi_major, self.semi_minor) 37 | 38 | 39 | # EK Attribute Description 40 | class EkAttDsc(object): 41 | def __init__(self): 42 | self.cclass = 0 43 | self.dtype = DataType() 44 | self.strlen = 0 45 | self.size = 0 46 | self.indexd = False 47 | self.nullok = False 48 | 49 | # EK Segment Summary 50 | class EkSegSum(object): 51 | def __init__(self): 52 | self.tabnam = '' 53 | self.nrows = 0 54 | self.ncols = 0 55 | self.cnames = [] # list of strings 56 | self.cdescrs = [] # list of EkAttDsc 57 | 58 | 59 | class Plane(object): 60 | def __init__(self, normal=[0.0]*3, constant=0.0): 61 | self.normal = normal 62 | self.constant = constant 63 | 64 | def __str__(self): 65 | return '' % (', '.join([str(x) for x in self.normal]), self.constant) 66 | 67 | -------------------------------------------------------------------------------- /tests/test_exception.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import spice 4 | 5 | class ExceptionTestCase(TestCase): 6 | def test_raise_spice_exception(self): 7 | def raise_it(): 8 | raise spice.SpiceException('testing') 9 | 10 | self.assertRaises(spice.SpiceException, raise_it) 11 | 12 | def test_spice_exception(self): 13 | self.assertRaises(spice.SpiceException, spice.furnsh, '/dev/null') 14 | -------------------------------------------------------------------------------- /tests/test_pyspice.py: -------------------------------------------------------------------------------- 1 | # Released under the BSD license, see LICENSE for details 2 | 3 | import os, sys, unittest 4 | from spice import Ellipse, Plane 5 | 6 | class TestFile(unittest.TestCase): 7 | def testEllipse(self): 8 | center = [234.3, 8348.3, 937.6] 9 | semi_major = [5.0, 293.4, 11.0] 10 | semi_minor = [6.0, 3.0, 6.0] 11 | 12 | ellipse = Ellipse( 13 | center=center, 14 | semi_minor=semi_minor, 15 | semi_major=semi_major 16 | ) 17 | 18 | self.assertTrue(ellipse.center == center) 19 | self.assertTrue(ellipse.semi_minor == semi_minor) 20 | self.assertTrue(ellipse.semi_major == semi_major) 21 | 22 | def testPlane(self): 23 | normal = [2.4, 34.5, 9.2] 24 | constant = 4.7 25 | 26 | p = Plane(normal, constant) 27 | 28 | self.assertTrue(p.normal == normal) 29 | self.assertTrue(p.constant == constant) 30 | 31 | 32 | if __name__ == '__main__': 33 | unittest.main() 34 | --------------------------------------------------------------------------------