|
1 #!/usr/bin/env python3 |
|
2 # -*- coding: utf-8 -*- |
|
3 |
|
4 # Copyright (c) 2003 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
|
5 # |
|
6 |
|
7 """ |
|
8 eric API Generator. |
|
9 |
|
10 This is the main Python script of the API generator. It is |
|
11 this script that gets called via the API generation interface. |
|
12 This script can be used via the commandline as well. |
|
13 """ |
|
14 |
|
15 import os |
|
16 import sys |
|
17 import glob |
|
18 import fnmatch |
|
19 |
|
20 sys.path.insert(1, os.path.dirname(__file__)) |
|
21 |
|
22 import Utilities.ModuleParser |
|
23 from DocumentationTools.APIGenerator import APIGenerator |
|
24 from UI.Info import Version |
|
25 import Utilities |
|
26 import DocumentationTools |
|
27 |
|
28 |
|
29 def usage(): |
|
30 """ |
|
31 Function to print some usage information. |
|
32 |
|
33 It prints a reference of all commandline parameters that may |
|
34 be used and ends the application. |
|
35 """ |
|
36 print("eric7_api") |
|
37 print() |
|
38 print("Copyright (c) 2004 - 2022 Detlev Offenbach" |
|
39 " <detlev@die-offenbachs.de>.") |
|
40 print() |
|
41 print("Usage:") |
|
42 print() |
|
43 print(" eric7_api [options] files...") |
|
44 print() |
|
45 print("where files can be either python modules, package") |
|
46 print("directories or ordinary directories.") |
|
47 print() |
|
48 print("Options:") |
|
49 print() |
|
50 print(" -b name or --base=name") |
|
51 print(" Use the given name as the name of the base package.") |
|
52 print(" -e eol-type or --eol=eol-type") |
|
53 print(" Use the given eol type to terminate lines.") |
|
54 print(" Valid values are 'cr', 'lf' and 'crlf'.") |
|
55 print(" --exclude-file=pattern") |
|
56 print(" Specify a filename pattern of files to be excluded.") |
|
57 print(" This option may be repeated multiple times.") |
|
58 print(" -h or --help") |
|
59 print(" Show this help and exit.") |
|
60 print(" -i or --ignore") |
|
61 print(" Ignore the set of builtin modules") |
|
62 print(" -l language or --language=language") |
|
63 print(" Generate an API file for the given programming language.") |
|
64 print(" Supported programming languages are:") |
|
65 for lang in sorted( |
|
66 DocumentationTools.supportedExtensionsDictForApis.keys()): |
|
67 print(" * {0}".format(lang)) |
|
68 print(" The default is 'Python3'.") |
|
69 print(" This option may be repeated multiple times.") |
|
70 print(" -o filename or --output=filename") |
|
71 print(" Write the API information to the named file." |
|
72 " A '%L' placeholder") # __IGNORE_WARNING_M601__ |
|
73 print(" is replaced by the language of the API file" |
|
74 " (see --language).") |
|
75 print(" -p or --private") |
|
76 print(" Include private methods and functions.") |
|
77 print(" -R, -r or --recursive") |
|
78 print(" Perform a recursive search for source files.") |
|
79 print(" -t ext or --extension=ext") |
|
80 print(" Add the given extension to the list of file extensions.") |
|
81 print(" This option may be given multiple times.") |
|
82 print(" -V or --version") |
|
83 print(" Show version information and exit.") |
|
84 print(" -x directory or --exclude=directory") |
|
85 print(" Specify a directory basename to be excluded.") |
|
86 print(" This option may be repeated multiple times.") |
|
87 sys.exit(1) |
|
88 |
|
89 |
|
90 def version(): |
|
91 """ |
|
92 Function to show the version information. |
|
93 """ |
|
94 print( |
|
95 """eric7_api {0}\n""" |
|
96 """\n""" |
|
97 """eric API generator.\n""" |
|
98 """\n""" |
|
99 """Copyright (c) 2004 - 2022 Detlev Offenbach""" |
|
100 """ <detlev@die-offenbachs.de>\n""" |
|
101 """This is free software; see the LICENSE.GPL3 for copying""" |
|
102 """ conditions.\n""" |
|
103 """There is NO warranty; not even for MERCHANTABILITY or FITNESS""" |
|
104 """ FOR A\n""" |
|
105 """PARTICULAR PURPOSE.""".format(Version)) |
|
106 sys.exit(1) |
|
107 |
|
108 |
|
109 def main(): |
|
110 """ |
|
111 Main entry point into the application. |
|
112 """ |
|
113 global supportedExtensions |
|
114 |
|
115 import getopt |
|
116 |
|
117 try: |
|
118 opts, args = getopt.getopt( |
|
119 sys.argv[1:], "b:e:hil:o:pRrt:Vx:", |
|
120 ["base=", "eol=", "exclude=", "exclude-file=", "extension=", |
|
121 "help", "ignore", "language=", "output=", "private", "recursive", |
|
122 "version", ]) |
|
123 except getopt.error: |
|
124 usage() |
|
125 |
|
126 excludeDirs = [".svn", ".hg", ".git", ".ropeproject", ".eric7project", |
|
127 "dist", "build", "doc", "docs"] |
|
128 excludePatterns = [] |
|
129 outputFileName = "" |
|
130 recursive = False |
|
131 basePackage = "" |
|
132 includePrivate = False |
|
133 progLanguages = [] |
|
134 extensions = [] |
|
135 newline = None |
|
136 ignoreBuiltinModules = False |
|
137 |
|
138 for k, v in opts: |
|
139 if k in ["-o", "--output"]: |
|
140 outputFileName = v |
|
141 elif k in ["-R", "-r", "--recursive"]: |
|
142 recursive = True |
|
143 elif k in ["-x", "--exclude"]: |
|
144 excludeDirs.append(v) |
|
145 elif k == "--exclude-file": |
|
146 excludePatterns.append(v) |
|
147 elif k in ["-h", "--help"]: |
|
148 usage() |
|
149 elif k in ["-i", "--ignore"]: |
|
150 ignoreBuiltinModules = True |
|
151 elif k in ["-V", "--version"]: |
|
152 version() |
|
153 elif k in ["-t", "--extension"]: |
|
154 if not v.startswith("."): |
|
155 v = ".{0}".format(v) |
|
156 extensions.append(v) |
|
157 elif k in ["-b", "--base"]: |
|
158 basePackage = v |
|
159 elif k in ["-p", "--private"]: |
|
160 includePrivate = True |
|
161 elif k in ["-l", "--language"]: |
|
162 if v not in progLanguages: |
|
163 if v not in DocumentationTools.supportedExtensionsDictForApis: |
|
164 sys.stderr.write( |
|
165 "Wrong language given: {0}. Aborting\n".format(v)) |
|
166 sys.exit(1) |
|
167 else: |
|
168 progLanguages.append(v) |
|
169 elif k in ["-e", "--eol"]: |
|
170 if v.lower() == "cr": |
|
171 newline = '\r' |
|
172 elif v.lower() == "lf": |
|
173 newline = '\n' |
|
174 elif v.lower() == "crlf": |
|
175 newline = '\r\n' |
|
176 |
|
177 if not args: |
|
178 usage() |
|
179 |
|
180 if outputFileName == "": |
|
181 sys.stderr.write("No output file given. Aborting\n") |
|
182 sys.exit(1) |
|
183 |
|
184 if len(progLanguages) == 0: |
|
185 progLanguages = ["Python3"] |
|
186 |
|
187 for progLanguage in sorted(progLanguages): |
|
188 basename = "" |
|
189 apis = [] |
|
190 basesDict = {} |
|
191 |
|
192 supportedExtensions = ( |
|
193 DocumentationTools.supportedExtensionsDictForApis[progLanguage] |
|
194 ) |
|
195 supportedExtensions.extend(extensions) |
|
196 |
|
197 if not outputFileName.endswith(".api"): |
|
198 # append the .api extension, if not given by the user |
|
199 outputFileName += ".api" |
|
200 if "%L" in outputFileName: |
|
201 outputFile = outputFileName.replace("%L", progLanguage) |
|
202 else: |
|
203 if len(progLanguages) == 1: |
|
204 outputFile = outputFileName |
|
205 else: |
|
206 root, ext = os.path.splitext(outputFileName) |
|
207 outputFile = "{0}-{1}{2}".format(root, progLanguage.lower(), |
|
208 ext) |
|
209 basesFile = os.path.splitext(outputFile)[0] + '.bas' |
|
210 |
|
211 for arg in args: |
|
212 if os.path.isdir(arg): |
|
213 if os.path.exists(os.path.join( |
|
214 arg, Utilities.joinext("__init__", ".py"))): |
|
215 basename = os.path.dirname(arg) |
|
216 if arg == '.': |
|
217 sys.stderr.write("The directory '.' is a package.\n") |
|
218 sys.stderr.write( |
|
219 "Please repeat the call giving its real name.\n") |
|
220 sys.stderr.write("Ignoring the directory.\n") |
|
221 continue |
|
222 else: |
|
223 basename = arg |
|
224 if basename: |
|
225 basename = "{0}{1}".format(basename, os.sep) |
|
226 |
|
227 if recursive and not os.path.islink(arg): |
|
228 names = [arg] + Utilities.getDirs(arg, excludeDirs) |
|
229 else: |
|
230 names = [arg] |
|
231 else: |
|
232 basename = "" |
|
233 names = [arg] |
|
234 |
|
235 for filename in sorted(names): |
|
236 inpackage = False |
|
237 if os.path.isdir(filename): |
|
238 files = [] |
|
239 for ext in supportedExtensions: |
|
240 files.extend(glob.glob(os.path.join( |
|
241 filename, Utilities.joinext("*", ext)))) |
|
242 initFile = os.path.join( |
|
243 filename, Utilities.joinext("__init__", ext)) |
|
244 if initFile in files: |
|
245 inpackage = True |
|
246 files.remove(initFile) |
|
247 files.insert(0, initFile) |
|
248 elif progLanguage != "Python3": |
|
249 # assume package |
|
250 inpackage = True |
|
251 else: |
|
252 if ( |
|
253 Utilities.isWindowsPlatform() and |
|
254 glob.has_magic(filename) |
|
255 ): |
|
256 files = glob.glob(filename) |
|
257 else: |
|
258 files = [filename] |
|
259 |
|
260 for file in files: |
|
261 skipIt = False |
|
262 for pattern in excludePatterns: |
|
263 if fnmatch.fnmatch(os.path.basename(file), pattern): |
|
264 skipIt = True |
|
265 break |
|
266 if skipIt: |
|
267 continue |
|
268 |
|
269 try: |
|
270 module = Utilities.ModuleParser.readModule( |
|
271 file, |
|
272 basename=basename, inpackage=inpackage, |
|
273 ignoreBuiltinModules=ignoreBuiltinModules) |
|
274 apiGenerator = APIGenerator(module) |
|
275 api = apiGenerator.genAPI(True, basePackage, |
|
276 includePrivate) |
|
277 bases = apiGenerator.genBases(includePrivate) |
|
278 except OSError as v: |
|
279 sys.stderr.write("{0} error: {1}\n".format(file, v[1])) |
|
280 continue |
|
281 except ImportError as v: |
|
282 sys.stderr.write("{0} error: {1}\n".format(file, v)) |
|
283 continue |
|
284 |
|
285 for apiEntry in api: |
|
286 if apiEntry not in apis: |
|
287 apis.append(apiEntry) |
|
288 for basesEntry in bases: |
|
289 if bases[basesEntry]: |
|
290 basesDict[basesEntry] = bases[basesEntry][:] |
|
291 sys.stdout.write("-- {0} -- {1} ok\n".format( |
|
292 progLanguage, file)) |
|
293 |
|
294 outdir = os.path.dirname(outputFile) |
|
295 if outdir and not os.path.exists(outdir): |
|
296 os.makedirs(outdir) |
|
297 try: |
|
298 with open(outputFile, "w", encoding="utf-8", |
|
299 newline=newline) as out: |
|
300 out.write("\n".join(sorted(apis)) + "\n") |
|
301 except OSError as v: |
|
302 sys.stderr.write("{0} error: {1}\n".format(outputFile, v[1])) |
|
303 sys.exit(3) |
|
304 try: |
|
305 with open(basesFile, "w", encoding="utf-8", |
|
306 newline=newline) as out: |
|
307 for baseEntry in sorted(basesDict.keys()): |
|
308 out.write("{0} {1}\n".format( |
|
309 baseEntry, " ".join(sorted(basesDict[baseEntry])))) |
|
310 except OSError as v: |
|
311 sys.stderr.write("{0} error: {1}\n".format(basesFile, v[1])) |
|
312 sys.exit(3) |
|
313 |
|
314 sys.stdout.write('\nDone.\n') |
|
315 sys.exit(0) |
|
316 |
|
317 if __name__ == '__main__': |
|
318 main() |
|
319 |
|
320 # |
|
321 # eflag: noqa = M801 |