PyXMake Developer Guide 1.0
PyXMake
Loading...
Searching...
No Matches
openapi.py
1# -*- coding: utf-8 -*-
2# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3# % PyXMake - Build environment for PyXMake %
4# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5"""
6Triple-use minimum working example for PyXMake. This script can be
7executed in three different ways in varying levels of accessibility
8
9@note: Create a Python package from an OpenAPI specification. Optionally, create a
10portable installer instead.
11
12Created on 21.10.2022
13
14@version: 1.0
15----------------------------------------------------------------------------------------------
16@requires:
17 - PyXMake
18
19@author: garb_ma [DLR-FA,STM Braunschweig]
20----------------------------------------------------------------------------------------------
21"""
22import os, sys
23import shutil
24import argparse
25import zipfile
26import time
27import subprocess
28import posixpath
29
30try:
31 import PyXMake as _ #@UnusedImport
32except ImportError:
33 # Script is executed as a plug-in
34 sys.path.insert(0,os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
35finally:
36 import PyXMake
37 from PyXMake.Tools import Utility #@UnresolvedImport
38 from PyXMake.Build.Make import AllowDefaultMakeOption
39
40try:
41 # Import PyCODAC to build library locally during setup.
42 import PyCODAC
43except ImportError: pass
44
45# Refer to requests here to avoid conda mangling
46import requests
47
48def main(
49 BuildID,
50 # URL to API specification
51 source="https://stmlab.fa-services.intra.dlr.de/2/openapi.json",
52 # Define output path
53 output=None,
54 # Additional keyword arguments
55 **kwargs):
56 """
57 Main function to execute the script.
58 """
59 # Definition of local variables
60 delimn = "."; result = -1; repeat = 0; client = kwargs.get("client","python")
61 # Default OpenAPI URL refers to modified version running on FA.
62 url = posixpath.join(kwargs.get("base_url","https://stmlab.fa-services.intra.dlr.de/2/PyXMake/api/client/zip"))
63 # Default output directory refers to current directory if not given.
64 if not output: output=os.getcwd()
65 # Assembly of request query structure. Only python generator is currently supported
66 data = {"URI":source,"ClientID": client,
67 "CLI": ["--skip-validate-spec",
68 "--additional-properties=packageName=%s" % str(BuildID),
69 "--additional-properties=packageVersion=%s" % str(kwargs.get("version","1.0.0")),
70 "--additional-properties=packageUrl=%s" % posixpath.dirname(source)]}
71 # Definition of output filename. Defaults to project name followed by default file extension.
72 filename = kwargs.get("filename",delimn.join([BuildID,kwargs.get("ext",posixpath.basename(url))]))
73 # Check if the URL can be reached. Raise an error if that is not the case.
74 try:
75 if not requests.head(url).ok: raise ValueError
76 except: raise ConnectionError("The given url cannot be reached: %s" % str(url))
77
78 # Fail gracefully
79 try:
80 # Attempt to send a patch request and download the result. The result is an archive.
81 with Utility.TemporaryDirectory() as _, requests.patch(url, params=data, stream=True) as r:
82 r.raise_for_status()
83 with open(filename, 'wb') as f:
84 for chunk in r.iter_content(chunk_size=8192): f.write(chunk)
85 # Return compiled Python wheels. Defaults to False.
86 if kwargs.get("build",False):
87 # Extract the archive
88 with zipfile.ZipFile(filename, 'r') as zip_ref: zip_ref.extractall()
89 with Utility.ChangedWorkingDirectory(os.path.join(os.path.abspath(os.getcwd()),client)):
90 # Execute build command
91 subprocess.check_call([sys.executable,"-m","build"]);
92 # Recreate the archive
93 Utility.CreateArchive(filename, os.path.join(os.path.abspath(os.getcwd()),client,"dist"))
94 # We have an result file. Copy it to the current output directory
95 if os.listdir(os.getcwd()):
96 while True:
97 try:
98 # Attempt to copy all result files
99 shutil.copy(filename, output); break
100 except:
101 # Catch potential race condition
102 repeat += 1;
103 # If no success after three attempts. throw an error.
104 if repeat >= 3: break;
105 else: time.sleep(2);
106 # Everything worked
107 result = 0
108 # Something went terribly wrong...
109 except ImportError: pass
110 # Present the outcome.
111 return result
112
113if __name__ == "__main__":
114# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
115# % Access command line inputs %
116# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
117 parser = argparse.ArgumentParser(description='CLI wrapper options for OpenAPI client generator.')
118 parser.add_argument('name', type=str, nargs=1, help="Name of the project")
119 parser.add_argument('source', type=str, nargs=1, help="URL to an OpenAPI specification")
120 parser.add_argument('--version', type=str, nargs=1, help="Version used during package creation. Defaults to 1.0.0")
121 parser.add_argument("--output", type=str, nargs=1, help="Absolute path to output directory. Defaults to current project folder.")
122 parser.add_argument("--file", type=str, nargs=1, help="Output file name. Defaults to Name.zip")
123 parser.add_argument("--build", type=Utility.GetBoolean, const=True, default=True, nargs='?',
124 help="Check public PyPi repository to verify the results. Defaults to True.")
125
126 # Command line separator
127 delimn = "."
128
129 try:
130 # Check CLI options
131 _ = sys.argv[1]
132 args, _ = parser.parse_known_args()
133 # Project name is mandatory
134 project = args.name[0]
135 # Specification is mandatory
136 source = args.source[0] ;
137 # Optional non-default version
138 try: version = args.version[0]
139 except: version = "1.0.0"
140 # Optional non-default output directory
141 try: output = args.output[0]
142 except: output = os.path.abspath(os.getcwd())
143 # Optional non-default build command. Defaults to True
144 try: build = args.build[0]
145 except: build = True
146 # Optional non-default output filename
147 try: filename = args.file[0]
148 except: filename = delimn.join([project,"zip"])
149
150 # Use an exception to allow help message to be printed.
151 except Exception as _:
152 # Run default test coverage of all integrated projects.
153 if AllowDefaultMakeOption:
154 try:
155 # Build an API client for PyCODAC
156 main("pyc_client", source="https://stmlab.fa-services.intra.dlr.de/1/openapi.json", output=os.getcwd(), version=PyCODAC.__version__, build=True)
157 except: pass # Fail gracefully
158 # Build API client for PyXMake.
159 main("pyx_client", source="https://stmlab.fa-services.intra.dlr.de/2/openapi.json", output=os.getcwd(), version=PyXMake.__version__, build=True)
160 else:
161 # Execute valid CLI command
162 main(project, source, output, filename=filename, version=version, build=build)
163
164 # Finish
165 print("==================================")
166 print("Finished building API client")
167 print("==================================")
168 sys.exit()
Class to create 2to3 compatible pickling dictionary.
Create a make object to define the building environment.
Definition Make.py:1
Module containing basic functionalities defined for convenience.
Definition __init__.py:1
main(BuildID, source="https://stmlab.fa-services.intra.dlr.de/2/openapi.json", output=None, **kwargs)
Definition openapi.py:55