blob: c532ea6232deea1810cdee3b97016396cb69a548 [file] [log] [blame]
Jack Jansen7571f301995-07-29 13:48:41 +00001"""Create an applet from a Python script.
2
3This puts up a dialog asking for a Python source file ('TEXT').
4The output is a file with the same name but its ".py" suffix dropped.
5It is created by copying an applet template and then adding a 'PYC '
6resource named __main__ containing the compiled, marshalled script.
7"""
8
9import sys
10sys.stdout = sys.stderr
11
12import string
13import os
14import marshal
15import imp
16import macfs
17import MacOS
18from Res import *
19
20# .pyc file (and 'PYC ' resource magic number)
21MAGIC = imp.get_magic()
22
23# Template file (searched on sys.path)
24TEMPLATE = "PythonApplet"
25
26# Specification of our resource
27RESTYPE = 'PYC '
28RESNAME = '__main__'
29
30# A resource with this name sets the "owner" (creator) of the destination
31OWNERNAME = "owner resource"
32
33# OpenResFile mode parameters
34READ = 1
35WRITE = 2
36
37def main():
38
39 # Find the template
40 # (there's no point in proceeding if we can't find it)
41
42 for p in sys.path:
43 template = os.path.join(p, TEMPLATE)
44 try:
45 tmpl = open(template, "rb")
46 tmpl.close()
47 break
48 except IOError:
49 continue
50 else:
51 die("Template %s not found" % `template`)
52 return
53
54 # Convert to full pathname
55 template = macfs.FSSpec(template).as_pathname()
56
57 # Ask for source text if not specified in sys.argv[1:]
58
59 if not sys.argv[1:]:
Jack Jansen9062fa21995-08-14 12:21:12 +000060 srcfss, ok = macfs.PromptGetFile('Select Python source file:', 'TEXT')
Jack Jansen7571f301995-07-29 13:48:41 +000061 if not ok:
62 return
63 filename = srcfss.as_pathname()
64 tp, tf = os.path.split(filename)
65 if tf[-3:] == '.py':
66 tf = tf[:-3]
67 else:
68 tf = tf + '.applet'
69 dstfss, ok = macfs.StandardPutFile('Save application as:', tf)
70 if not ok: return
71 process(template, filename, dstfss.as_pathname())
72 else:
73
74 # Loop over all files to be processed
75 for filename in sys.argv[1:]:
76 process(template, filename, '')
77
78undefs = ('Atmp', '????', ' ', '\0\0\0\0', 'BINA')
79
80def process(template, filename, output):
81
82 print "Processing", `filename`, "..."
83
84 # Read the source and compile it
85 # (there's no point overwriting the destination if it has a syntax error)
86
87 fp = open(filename)
88 text = fp.read()
89 fp.close()
90 try:
91 code = compile(text, filename, "exec")
92 except (SyntaxError, EOFError):
93 die("Syntax error in script %s" % `filename`)
94 return
95
96 # Set the destination file name
97
98 if string.lower(filename[-3:]) == ".py":
99 destname = filename[:-3]
100 rsrcname = destname + '.rsrc'
101 else:
102 destname = filename + ".applet"
103 rsrcname = filename + '.rsrc'
104
105 if output:
106 destname = output
107 # Copy the data from the template (creating the file as well)
108
109 tmpl = open(template, "rb")
110 dest = open(destname, "wb")
111 data = tmpl.read()
112 if data:
113 dest.write(data)
114 dest.close()
115 tmpl.close()
116
117 # Copy the creator of the template to the destination
118 # unless it already got one. Set type to APPL
119
120 tctor, ttype = MacOS.GetCreatorAndType(template)
121 ctor, type = MacOS.GetCreatorAndType(destname)
122 if type in undefs: type = 'APPL'
123 if ctor in undefs: ctor = tctor
124
125 # Open the output resource fork
126
127 try:
128 output = FSpOpenResFile(destname, WRITE)
129 except MacOS.Error:
130 print "Creating resource fork..."
131 CreateResFile(destname)
132 output = FSpOpenResFile(destname, WRITE)
133
134 # Copy the resources from the template
135
136 input = FSpOpenResFile(template, READ)
137 newctor = copyres(input, output)
138 CloseResFile(input)
139 if newctor: ctor = newctor
140
141 # Copy the resources from the target specific resource template, if any
142
143 try:
144 input = FSpOpenResFile(rsrcname, READ)
145 except MacOS.Error:
146 pass
147 else:
148 newctor = copyres(input, output)
149 CloseResFile(input)
150 if newctor: ctor = newctor
151
152 # Now set the creator and type of the destination
153
154 MacOS.SetCreatorAndType(destname, ctor, type)
155
156 # Make sure we're manipulating the output resource file now
157
158 UseResFile(output)
159
160 # Delete any existing 'PYC 'resource named __main__
161
162 try:
163 res = Get1NamedResource(RESTYPE, RESNAME)
164 res.RemoveResource()
165 except Error:
166 pass
167
168 # Create the raw data for the resource from the code object
169
170 data = marshal.dumps(code)
171 del code
172 data = (MAGIC + '\0\0\0\0') + data
173
174 # Create the resource and write it
175
176 id = 0
177 while id < 128:
178 id = Unique1ID(RESTYPE)
179 res = Resource(data)
180 res.AddResource(RESTYPE, id, RESNAME)
181 res.WriteResource()
182 res.ReleaseResource()
183
184 # Close the output file
185
186 CloseResFile(output)
187
188 # Give positive feedback
189
190 message("Applet %s created." % `destname`)
191
192
193# Copy resources between two resource file descriptors.
194# Exception: don't copy a __main__ resource.
195# If a resource's name is "owner resource", its type is returned
196# (so the caller can use it to set the destination's creator)
197
198def copyres(input, output):
199 ctor = None
200 UseResFile(input)
201 ntypes = Count1Types()
202 for itype in range(1, 1+ntypes):
203 type = Get1IndType(itype)
204 nresources = Count1Resources(type)
205 for ires in range(1, 1+nresources):
206 res = Get1IndResource(type, ires)
207 id, type, name = res.GetResInfo()
208 lcname = string.lower(name)
209 if (type, lcname) == (RESTYPE, RESNAME):
210 continue # Don't copy __main__ from template
211 if lcname == OWNERNAME: ctor = type
212 size = res.size
213 attrs = res.GetResAttrs()
214 print id, type, name, size, hex(attrs)
215 res.LoadResource()
216 res.DetachResource()
217 UseResFile(output)
218 try:
219 res2 = Get1Resource(type, id)
220 except MacOS.Error:
221 res2 = None
222 if res2:
223 print "Overwriting..."
224 res2.RemoveResource()
225 res.AddResource(type, id, name)
226 res.WriteResource()
227 attrs = attrs | res.GetResAttrs()
228 print "New attrs =", hex(attrs)
229 res.SetResAttrs(attrs)
230 UseResFile(input)
231 return ctor
232
233
234# Show a message and exit
235
236def die(str):
237 message(str)
238 sys.exit(1)
239
240
241# Show a message
242
243def message(str, id = 256):
244 from Dlg import *
245 d = GetNewDialog(id, -1)
246 if not d:
247 print "Error:", `str`
248 print "DLOG id =", id, "not found."
249 return
250 tp, h, rect = d.GetDialogItem(2)
251 SetDialogItemText(h, str)
252 while 1:
253 n = ModalDialog(None)
254 if n == 1: break
255 del d
256
257
258if __name__ == '__main__':
259 main()
260