diff --git a/src/main/resources/python3/__init__.mustache b/src/main/resources/python3/__init__.mustache new file mode 100644 index 00000000000..4b41ee706c7 --- /dev/null +++ b/src/main/resources/python3/__init__.mustache @@ -0,0 +1,9 @@ +#!/usr/bin/env python +"""Add all of the modules in the current directory to __all__""" +import os + +__all__ = [] + +for module in os.listdir(os.path.dirname(__file__)): + if module != '__init__.py' and module[-3:] == '.py': + __all__.append(module[:-3]) diff --git a/src/main/resources/python3/api.mustache b/src/main/resources/python3/api.mustache new file mode 100644 index 00000000000..9d2f459fa2a --- /dev/null +++ b/src/main/resources/python3/api.mustache @@ -0,0 +1,96 @@ +#!/usr/bin/env python +""" +WordAPI.py +Copyright 2012 Wordnik, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +NOTE: This class is auto generated by the swagger code generator program. Do not edit the class manually. +""" +import sys +import os + +from .models import * + + +{{#operations}} +class {{classname}}(object): + + def __init__(self, apiClient): + self.apiClient = apiClient + + {{newline}} + {{#operation}} + def {{nickname}}(self, {{#requiredParams}}{{paramName}}{{#defaultValue}} = None{{/defaultValue}}, {{/requiredParams}}**kwargs): + """{{summary}} + + Args: + {{#allParams}} + {{paramName}}, {{dataType}}: {{description}} {{^optional}}(required){{/optional}}{{#optional}}(optional){{/optional}} + {{newline}} + {{/allParams}} + {{newline}} + Returns: {{returnType}} + """ + + allParams = [{{#allParams}}'{{paramName}}'{{#hasMore}}, {{/hasMore}}{{/allParams}}] + + params = locals() + for (key, val) in params['kwargs'].items(): + if key not in allParams: + raise TypeError("Got an unexpected keyword argument '%s' to method {{nickname}}" % key) + params[key] = val + del params['kwargs'] + + resourcePath = '{{path}}' + resourcePath = resourcePath.replace('{format}', 'json') + method = '{{httpMethod}}' + + queryParams = {} + headerParams = {} + + {{#queryParams}} + if ('{{paramName}}' in params): + queryParams['{{paramName}}'] = self.apiClient.toPathValue(params['{{paramName}}']) + {{/queryParams}} + + {{#headerParams}} + if ('{{paramName}}' in params): + headerParams['{{paramName}}'] = params['{{paramName}}'] + {{/headerParams}} + + {{#pathParams}} + if ('{{paramName}}' in params): + replacement = str(self.apiClient.toPathValue(params['{{paramName}}'])) + resourcePath = resourcePath.replace('{' + '{{baseName}}' + '}', + replacement) + {{/pathParams}} + + postData = (params['body'] if 'body' in params else None) + + response = self.apiClient.callAPI(resourcePath, method, queryParams, + postData, headerParams) + + {{#returnType}} + if not response: + return None + + responseObject = self.apiClient.deserialize(response, '{{returnType}}') + return responseObject + {{/returnType}} + {{newline}} + {{newline}} + {{/operation}} +{{newline}} +{{/operations}} +{{newline}} diff --git a/src/main/resources/python3/model.mustache b/src/main/resources/python3/model.mustache new file mode 100644 index 00000000000..94b7d25fd02 --- /dev/null +++ b/src/main/resources/python3/model.mustache @@ -0,0 +1,40 @@ +#!/usr/bin/env python +""" +Copyright 2012 Wordnik, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +{{#models}} +{{#model}} + +class {{classname}}: + """NOTE: This class is auto generated by the swagger code generator program. + Do not edit the class manually.""" + + + def __init__(self): + self.swaggerTypes = { + {{#vars}} + '{{name}}': '{{{datatype}}}'{{#hasMore}}, + {{/hasMore}} + {{/vars}}{{newline}} + } + + + {{#vars}} + {{#description}}#{{description}} + {{/description}} + self.{{name}} = None # {{{datatype}}} + {{/vars}} +{{/model}} +{{/models}} diff --git a/src/main/resources/python3/swagger.mustache b/src/main/resources/python3/swagger.mustache new file mode 100644 index 00000000000..d31c0161611 --- /dev/null +++ b/src/main/resources/python3/swagger.mustache @@ -0,0 +1,194 @@ +#!/usr/bin/env python +"""Wordnik.com's Swagger generic API client. This client handles the client- +server communication, and is invariant across implementations. Specifics of +the methods and models for each application are generated from the Swagger +templates.""" + +import sys +import os +import re +import urllib.request, urllib.error, urllib.parse +import http.client +import json +import datetime + +from .models import * + + +class ApiClient: + """Generic API client for Swagger client library builds""" + + def __init__(self, apiKey=None, apiServer=None): + if apiKey == None: + raise Exception('You must pass an apiKey when instantiating the ' + 'APIClient') + self.apiKey = apiKey + self.apiServer = apiServer + self.cookie = None + + def callAPI(self, resourcePath, method, queryParams, postData, + headerParams=None): + + url = self.apiServer + resourcePath + headers = {} + if headerParams: + for param, value in headerParams.items(): + headers[param] = value + + headers['Content-type'] = 'application/json' + headers['api_key'] = self.apiKey + + if self.cookie: + headers['Cookie'] = self.cookie + + data = None + + if method == 'GET': + + if queryParams: + # Need to remove None values, these should not be sent + sentQueryParams = {} + for param, value in queryParams.items(): + if value != None: + sentQueryParams[param] = value + url = url + '?' + urllib.parse.urlencode(sentQueryParams) + + elif method in ['POST', 'PUT', 'DELETE']: + + if postData: + headers['Content-type'] = 'application/json' + data = self.sanitizeForSerialization(postData) + data = json.dumps(data) + + else: + raise Exception('Method ' + method + ' is not recognized.') + + if data: + data = data.encode('utf-8') + + requestParams = MethodRequest(method=method, url=url, + headers=headers, data=data) + + # Make the request + request = urllib.request.urlopen(requestParams) + encoding = request.headers.get_content_charset() + if not encoding: + encoding = 'iso-8859-1' + response = request.read().decode(encoding) + + try: + data = json.loads(response) + except ValueError: # PUT requests don't return anything + data = None + + return data + + def toPathValue(self, obj): + """Serialize a list to a CSV string, if necessary. + Args: + obj -- data object to be serialized + Returns: + string -- json serialization of object + """ + if type(obj) == list: + return ','.join(obj) + else: + return obj + + def sanitizeForSerialization(self, obj): + """Dump an object into JSON for POSTing.""" + + if not obj: + return None + elif type(obj) in [str, int, float, bool]: + return obj + elif type(obj) == list: + return [self.sanitizeForSerialization(subObj) for subObj in obj] + elif type(obj) == datetime.datetime: + return obj.isoformat() + else: + if type(obj) == dict: + objDict = obj + else: + objDict = obj.__dict__ + return {key: self.sanitizeForSerialization(val) + for (key, val) in objDict.items() + if key != 'swaggerTypes'} + + def deserialize(self, obj, objClass): + """Derialize a JSON string into an object. + + Args: + obj -- string or object to be deserialized + objClass -- class literal for deserialzied object, or string + of class name + Returns: + object -- deserialized object""" + + # Have to accept objClass as string or actual type. Type could be a + # native Python type, or one of the model classes. + if type(objClass) == str: + if 'list[' in objClass: + match = re.match('list\[(.*)\]', objClass) + subClass = match.group(1) + return [self.deserialize(subObj, subClass) for subObj in obj] + + if (objClass in ['int', 'float', 'dict', 'list', 'str']): + objClass = eval(objClass) + else: # not a native type, must be model class + objClass = eval(objClass + '.' + objClass) + + if objClass in [str, int, float, bool]: + return objClass(obj) + elif objClass == datetime: + # Server will always return a time stamp in UTC, but with + # trailing +0000 indicating no offset from UTC. So don't process + # last 5 characters. + return datetime.datetime.strptime(obj[:-5], + "%Y-%m-%dT%H:%M:%S.%f") + + instance = objClass() + + for attr, attrType in instance.swaggerTypes.items(): + + if attr in obj: + value = obj[attr] + if attrType in ['str', 'int', 'float', 'bool']: + attrType = eval(attrType) + try: + value = attrType(value) + except UnicodeEncodeError: + value = unicode(value) + setattr(instance, attr, value) + elif 'list[' in attrType: + match = re.match('list\[(.*)\]', attrType) + subClass = match.group(1) + subValues = [] + if not value: + setattr(instance, attr, None) + else: + for subValue in value: + subValues.append(self.deserialize(subValue, + subClass)) + setattr(instance, attr, subValues) + else: + setattr(instance, attr, self.deserialize(value, + objClass)) + + return instance + + +class MethodRequest(urllib.request.Request): + + def __init__(self, *args, **kwargs): + """Construct a MethodRequest. Usage is the same as for + `urllib.Request` except it also takes an optional `method` + keyword argument. If supplied, `method` will be used instead of + the default.""" + + if 'method' in kwargs: + self.method = kwargs.pop('method') + return urllib.request.Request.__init__(self, *args, **kwargs) + + def get_method(self): + return getattr(self, 'method', urllib.request.Request.get_method(self)) diff --git a/src/main/scala/Python3PetstoreCodegen.scala b/src/main/scala/Python3PetstoreCodegen.scala new file mode 100644 index 00000000000..9f4171b19d0 --- /dev/null +++ b/src/main/scala/Python3PetstoreCodegen.scala @@ -0,0 +1,17 @@ +import com.wordnik.swagger.codegen.BasicPython3Generator + +import com.wordnik.swagger.core._ + +import java.io.File + +object Python3PetstoreCodegen extends BasicPython3Generator { + def main(args: Array[String]) = generateClient(args) + + override def templateDir = "python3" + override def destinationDir = "samples/petstore/python3" + + override def supportingFiles = List( + ("__init__.mustache", destinationDir, "__init__.py"), + ("swagger.mustache", destinationDir + File.separator + apiPackage.get, "swagger.py"), + ("__init__.mustache", destinationDir + File.separator + modelPackage.get, "__init__.py")) +} diff --git a/src/main/scala/com/wordnik/swagger/codegen/BasicPython3Generator.scala b/src/main/scala/com/wordnik/swagger/codegen/BasicPython3Generator.scala new file mode 100644 index 00000000000..45da9a4c8a8 --- /dev/null +++ b/src/main/scala/com/wordnik/swagger/codegen/BasicPython3Generator.scala @@ -0,0 +1,48 @@ +/** + * Copyright 2012 Wordnik, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.wordnik.swagger.codegen + +import com.wordnik.swagger.core._ + +import java.io.File + +object BasicPython3Generator extends BasicPython3Generator { + def main(args: Array[String]) = generateClient(args) +} + +class BasicPython3Generator extends BasicPythonGenerator { + + // location of templates + override def templateDir = "python3" + + // where to write generated code + override def destinationDir = "generated-code/python3" + + // Python3 erases int/long distinction + override def typeMapping = Map( + "String" -> "str", + "Int" -> "int", + "Float" -> "float", + "Long" -> "int", + "long" -> "int", + "Double" -> "float", + "Array" -> "list", + "Boolean" -> "bool", + "Date" -> "datetime", + "string" -> "str" + ) +}