PK!# '55feisty/__init__.py__version__ = '0.1.0-pre' from .decorators import * PK!(x..feisty/__main__.pyfrom . import command_line command_line.main()PK!$תfeisty/command_line.pyimport argparse import importlib import os import re import sys import feisty import feisty.generate class CommandLineError(Exception): def __init__(self, error): self.error = error def _feisty_command_line(argv): parser = argparse.ArgumentParser() parser.add_argument('api_object') parser.add_argument('--debug', type=bool) if len(argv) > 0 and argv[0].endswith('.py'): argv.pop(0) args = parser.parse_args(argv) try: m = re.match( r'^([a-zA-Z0-9_.]+):([a-zA-Z0-9_.]+)', args.api_object) _mod, _sym = m.groups() except (ValueError, AttributeError): raise CommandLineError( 'Please specify the location of your Falcon API ' 'object in the form some.module:obj') try: mod = importlib.import_module(_mod) api = getattr(mod, _sym) except (ImportError, AttributeError): raise CommandLineError( 'Could not locate the API object {}'.format(args.api_object)) try: spec = feisty.generate.generate_schema(api) sys.stdout.write(spec.to_yaml()) sys.exit(0) except ValueError as e: raise CommandLineError(e.args[0]) def main(): try: _feisty_command_line(sys.argv) except CommandLineError as e: if os.environ.get('DEBUG'): raise else: sys.stderr.write(e.error + '\n') sys.exit(1) PK!$ feisty/decorators.pyimport functools import falcon import marshmallow class ValidationError(falcon.HTTPBadRequest): def __init__(self, errors): super(ValidationError, self).__init__(falcon.HTTP_400) self.errors = errors def to_dict(self, obj_type=dict): return {'errors': self.errors} def request_schema(schema_or_cls, enforce=False, load_from='auto'): def decorator(f): if load_from not in ('query', 'body', 'auto'): raise ValueError( 'load_from must be one of "query", "body", or "auto') schema = _get_schema_instance(schema_or_cls) if load_from == 'query' or ( load_from == 'auto' and f.__name__ == 'on_get'): load_attr = 'params' elif load_from == 'body' or ( load_from == 'auto' and f.__name__ == 'on_post'): load_attr = 'media' else: raise ValueError( 'Don\'t know how to load data from a {} handler. ' 'Specify load_from manually.') f._feisty_request_schema = schema f._feisty_request_schema_in = ( 'query' if load_attr == 'params' else 'body') @functools.wraps(f) def _wrapped(self, req, resp, *args, **kwargs): data = getattr(req, load_attr) if enforce: result = schema.load(data) if result.errors: raise ValidationError(result.errors) data = result.data else: data = req.media return f(self, req, resp, data, *args, **kwargs) return _wrapped return decorator def response_schema(schema_or_cls, enforce=True): schema = _get_schema_instance(schema_or_cls) def decorator(f): f._feisty_response_schema = schema @functools.wraps(f) def _wrapped(self, req, resp, *args, **kwargs): ret = f(self, req, resp, *args, **kwargs) if enforce: result = schema.dump(resp.media) resp.media = result.data return ret return _wrapped return decorator def _get_schema_instance(schema_or_cls): if isinstance(schema_or_cls, type): schema = schema_or_cls() elif isinstance(schema_or_cls, marshmallow.Schema): schema = schema_or_cls else: raise TypeError( 'schema_or_class must be a marshmallow Schema class or instance') return schema PK!vj__feisty/generate.pyimport functools import os import apispec import apispec.ext.marshmallow import marshmallow import yaml class FeistyConfigSchema(marshmallow.Schema): title = marshmallow.fields.String(required=True) version = marshmallow.fields.String(required=True) def generate_schema(api, config=None): if not config: cwd = os.getcwd() try: with open(os.path.join(cwd, '.feistyrc.yaml')) as f: config = yaml.load(f) except (IOError, OSError): raise ValueError('Could not find .feistyrc.yaml file') schema = FeistyConfigSchema() result = schema.load(config) if result.errors: raise ValueError('Invalid Feisty config: {}'.format(result.errors)) config = result.data spec = apispec.APISpec( config['title'], str(config['version']), openapi_version='2.0', plugins=(apispec.ext.marshmallow.MarshmallowPlugin(),)) converter = apispec.ext.marshmallow.OpenAPIConverter('2.0') for node in api._router._roots: ops = {} for method, f in node.method_map.items(): if (method.lower() not in ['get', 'put', 'post', 'delete'] or isinstance(f, functools.partial)): continue req_schema = getattr(f, '_feisty_request_schema', None) resp_schema = getattr(f, '_feisty_response_schema', None) op = {'parameters': [], 'responses': {200: {}}} if req_schema: op['parameters'] = converter.schema2parameters( req_schema, default_in=f._feisty_request_schema_in) if resp_schema: op['responses'][200]['schema'] = converter.schema2jsonschema( resp_schema) ops[method.lower()] = op spec.add_path(node.uri_template, ops) return spec PK!Hou03'feisty-0.2.2.dist-info/entry_points.txtN+I/N.,()JK,.Pzy)9yVy\\PK!HlŃTTfeisty-0.2.2.dist-info/WHEEL A н#J@Z|Jmqvh&#hڭw!Ѭ"J˫( } %PK!HVy 8feisty-0.2.2.dist-info/METADATAK0WܣBS,p(NpP&' KF3Xæ4-8 vGk➋Ú[!6֝_`l=jX[4&,k5hL\$2Q7 :*Eqk [Vm l瀌/%SyNv';.&;.smqhrp\D2D2ik d?/469z<~PK!H\feisty-0.2.2.dist-info/RECORDuι@἟, P@z@DN8 R@QO?'䏾sl CL0 y9qˈuE[*C&&qtBe H@? zDipԐ]Xʵd_5mhzS&MA"s9ۛZuVsÖ+m DNuU5'r;G,}Е~]㡮#Wމ, U]Y]yR;myòԪz('s:yz4)ߗ!6 D cus>PK!# '55feisty/__init__.pyPK!(x..efeisty/__main__.pyPK!$תfeisty/command_line.pyPK!$ feisty/decorators.pyPK!vj__feisty/generate.pyPK!Hou03'feisty-0.2.2.dist-info/entry_points.txtPK!HlŃTTfeisty-0.2.2.dist-info/WHEELPK!HVy 8feisty-0.2.2.dist-info/METADATAPK!H\_feisty-0.2.2.dist-info/RECORDPK }S