From 0282da6e3dff6653f68b5af4994b3f95e418665c Mon Sep 17 00:00:00 2001 From: Maxime Alves LIRMM Date: Thu, 23 Jul 2020 17:54:47 +0200 Subject: [PATCH] [release][0.1.0] First HalfAPI release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Squashed commit of the following: commit 68032dc55a07565ccd17a188407d9ac2537b62e6 Author: Maxime Alves LIRMM Date: Thu Jul 23 15:40:26 2020 +0200 [release][0.1.0] First HalfAPI release commit a046a81114a3ae22bbc84a53f1e85217a0954dbc Author: Maxime Alves LIRMM Date: Thu Jul 23 15:26:15 2020 +0200 [doc] màj du readme commit ed45b3011125c071aa53df8087d28bfa1150b373 Author: Maxime Alves LIRMM Date: Thu Jul 23 11:03:17 2020 +0200 [wip] rm apidb commit 7df4b9bacf3d26f09ea07856587505f74284cab4 Author: Maxime Alves LIRMM Date: Thu Jul 23 10:58:46 2020 +0200 [db] forgot foreign key router->domain commit 604b9a90f405121725e4b2126d73a5a83eec398f Author: Maxime Alves LIRMM Date: Thu Jul 23 10:51:01 2020 +0200 [wip] routes mounting fixed commit c50a5572633d7dcc3cad48ef79c5ea1ca098284d Author: Maxime Alves LIRMM Date: Thu Jul 23 10:29:09 2020 +0200 [wip][db][nf] http_verb, fct_name are in api.route commit 2b5b78db2f9c280dd5eb9d8bafc9174d9afc092f Author: Maxime Alves LIRMM Date: Wed Jul 22 17:37:21 2020 +0200 [wip] refactor du 22 juillet 2222 commit d019b7e333ab37f106895c84521cef1bfe768caa Author: Maxime Alves LIRMM Date: Wed Jul 22 14:48:51 2020 +0200 [wip] remove "version" from app commit 98ccd61dcf369b8c4aac817a0c8409b6fed00f50 Author: Joël Maïzi Date: Wed Jul 22 14:45:28 2020 +0200 Remove api.version from database. commit aa0d4f8dbba8b8ec878835bb58b69facff7e675e Author: Maxime Alves LIRMM@home Date: Tue Jul 21 21:46:22 2020 +0200 [db] added router as api.acl primary_key part commit a97984e9de0e6a00bddca7dece0c715c9c16cbe1 Author: Maxime Alves LIRMM@home Date: Tue Jul 21 21:41:31 2020 +0200 [wip] moved all acl treatment to acl_caller_middleware, fix route mounting, fix typo in logs commit 3dd310e80aaf6cb32f6c4ac23c1e2a924cebfde1 Author: Maxime Alves LIRMM Date: Tue Jul 21 13:10:26 2020 +0200 [wip][nf] gestion des routes avec routers commit c2687c4a24126fbc3e57257bf23c267b334df522 Author: Maxime Alves LIRMM Date: Tue Jul 21 12:39:53 2020 +0200 [db] ajout de la table api.router commit 9a10f76cf7790f75f23b72e19b0a58978752565c Author: Maxime Alves LIRMM Date: Tue Jul 21 12:19:53 2020 +0200 [db] renommage des champs d'acl commit c4e8c26a24835559d2e9b251df0eb462fe7e667d Author: Maxime Alves LIRMM Date: Tue Jul 21 12:13:38 2020 +0200 [wip][nf] modification du systeme de montage des routes commit b7e8352ba1e427e9883797a44bb0f3da5edd576d Author: Maxime Alves LIRMM Date: Tue Jul 21 11:15:30 2020 +0200 [wip][dbupdate] insertion des routes au nouveau format commit 28947444c4c062e6ced74f9bfdef11a36ddc438b Author: Maxime Alves LIRMM Date: Tue Jul 21 10:32:57 2020 +0200 [wip][dbupdate] Suppression des routes hors domaine --- CHANGELOG.md | 8 + LICENSE | 14 + README.md | 28 +- conf/systemd/halfapi_example_api | 10 + conf/systemd/halfapi_example_api.service | 19 + ..._api.socket => halfapi_example_api.socket} | 8 +- conf/systemd/lirmm_api.service | 26 - halfapi/__init__.py | 49 +- halfapi/app.py | 109 +-- halfapi/cli.py | 208 ++--- halfapi/config.py | 10 - halfapi/lib/acl_caller_middleware.py | 159 ---- halfapi/lib/acl_middleware.py | 45 - halfapi/lib/routes.py | 74 ++ halfapi/models/__init__.py | 11 - halfapi/models/api.sql | 61 +- halfapi/models/api/__init__.py | 15 - halfapi/models/api/acl.py | 52 -- halfapi/models/api/acl_function.py | 50 -- halfapi/models/api/domain.py | 50 -- halfapi/models/api/route.py | 49 - halfapi/models/api/version.py | 47 - halfapi/models/api/view/__init__.py | 11 - halfapi/models/api/view/acl.py | 49 - halfapi/models/api/view/route.py | 48 - halfapi/models/db_connector.py | 17 - poetry.lock | 848 ------------------ pyproject.toml | 35 - requirements.txt | 1 - setup.py | 62 ++ 30 files changed, 366 insertions(+), 1807 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 conf/systemd/halfapi_example_api create mode 100644 conf/systemd/halfapi_example_api.service rename conf/systemd/{lirmm_api.socket => halfapi_example_api.socket} (51%) delete mode 100644 conf/systemd/lirmm_api.service delete mode 100644 halfapi/config.py delete mode 100644 halfapi/lib/acl_caller_middleware.py delete mode 100644 halfapi/lib/acl_middleware.py create mode 100644 halfapi/lib/routes.py delete mode 100644 halfapi/models/__init__.py delete mode 100644 halfapi/models/api/__init__.py delete mode 100644 halfapi/models/api/acl.py delete mode 100644 halfapi/models/api/acl_function.py delete mode 100644 halfapi/models/api/domain.py delete mode 100644 halfapi/models/api/route.py delete mode 100644 halfapi/models/api/version.py delete mode 100644 halfapi/models/api/view/__init__.py delete mode 100644 halfapi/models/api/view/acl.py delete mode 100644 halfapi/models/api/view/route.py delete mode 100644 halfapi/models/db_connector.py delete mode 100644 poetry.lock delete mode 100644 pyproject.toml create mode 100755 setup.py diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..24ac252 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# HalfAPI + +## 0.1.0 + +- Mounts domain routers with their ACLs as decorator +- Configuration example files for systemd and a system-wide halfapi install +- Runs projects +- Handles JWT authentication middleware diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..dfa751b --- /dev/null +++ b/LICENSE @@ -0,0 +1,14 @@ +Copyright (c) 2015-2020 Joël Maïzi + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . diff --git a/README.md b/README.md index 781d37c..b425909 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # HalfAPI - This Python-based ASGI application aims to provide the core functionality to multiple API domains. @@ -13,33 +12,27 @@ The name "halfAPI" comes from the deep relationship between it and You'll need a database with the API details. You can find the database model in halfapi/models/api.sql -With halfORM's "hop" command, we generate the models that describe the database from the database schema itself. You can find the details in [hop_api](https://gite.lirmm.fr/newsi/db/hop_api) repository. - - **NOTE : The authentication module is deeply linked with [auth_lirmm](https://gite.lirmm.fr/newsi/auth_lirmm), so if you need to use the acl.connected function, you will need a running auth_lirmm server to get a token.** **TODO :** include a token generation tool for testing purpose. - ## Dependencies - python3 - python3-pip -- python3-virtualenv -- python3-venv - - -### pip - - poetry - +- uvicorn ## Installing -As the project uses the [poetry]() package manager, you first have to install it globally. It will replace virtualenv, pip, etc... +`pip install .` + +As the project uses the [poetry]() package manager, you first have to install it +for the current user. Be sur to have pip binary directory ($HOME/.local/bin) +included in your PATH. `pip3 install poetry` @@ -47,15 +40,6 @@ As the project uses the [poetry]() package manager, you first have to install it Be sur to include the bin directory of pip in your PATH. -### Virtual Environment (optional) -Then, cd in the halfapi repo, and chose your python version : - - -`POETRY_VIRTUALENVS_PATH=$HOME/.pyvenv poetry env use 3.7` - - -**NOTE : The virtualenv will be automatically be activated each time you run a command with the `poetry` tool. If you want to do it the classical way, or even without virtual environment, it's up to your choice.** - Installation of the deps : diff --git a/conf/systemd/halfapi_example_api b/conf/systemd/halfapi_example_api new file mode 100644 index 0000000..ee078ae --- /dev/null +++ b/conf/systemd/halfapi_example_api @@ -0,0 +1,10 @@ +GUNICORN_CMD_ARGS="--daemon \ + --bind unix:/var/lib/halfapi/example_api.sock \ + --max-requests 200 \ + --max-requests-jitter 20 \ + --workers 4 \ + --log-syslog-facility daemon \ + --worker-class uvicorn.workers.UvicornWorker + +HALFORM_CONF_DIR=/etc/half_orm +HALFAPI_CONF_DIR=/etc/half_api diff --git a/conf/systemd/halfapi_example_api.service b/conf/systemd/halfapi_example_api.service new file mode 100644 index 0000000..638bded --- /dev/null +++ b/conf/systemd/halfapi_example_api.service @@ -0,0 +1,19 @@ +[Unit] +Description=HalfAPI - Project : Example API Service +Requires=halfapi_example_api.socket +After=network.target + +[Service] +Type=simple +User=halfapi +Group=halfapi +WorkingDirectory=/var/lib/halfapi/example_api +EnvironmentFile=/etc/default/gunicorn/halfapi_example_api +ExecStart=/usr/bin/env gunicorn halfapi +ExecReload=/bin/kill -s HUP $MAINPID +KillMode=mixed +TimeoutStopSec=5 +PrivateTmp=true + +[Install] +WantedBy=multi-user.target diff --git a/conf/systemd/lirmm_api.socket b/conf/systemd/halfapi_example_api.socket similarity index 51% rename from conf/systemd/lirmm_api.socket rename to conf/systemd/halfapi_example_api.socket index c8c0e40..9dad8dd 100644 --- a/conf/systemd/lirmm_api.socket +++ b/conf/systemd/halfapi_example_api.socket @@ -1,10 +1,10 @@ [Unit] -Description=uvicorn socket +Description=HalfAPI - Project : Example API Socket [Socket] -ListenStream=/var/lib/api/lirmm_api.sock -User=api -SocketUser=api +ListenStream=/var/lib/halfapi/example_api.sock +User=halfapi +SocketUser=halfapi SocketGroup=www-data # Optionally restrict the socket permissions even more. # Mode=600 diff --git a/conf/systemd/lirmm_api.service b/conf/systemd/lirmm_api.service deleted file mode 100644 index 916ad47..0000000 --- a/conf/systemd/lirmm_api.service +++ /dev/null @@ -1,26 +0,0 @@ -[Unit] -Description=LIRMM API daemon -Requires=lirmm_api.socket -After=network.target - -[Service] -Type=simple -# the specific user that our service will run as -User=api -Group=www-data -# another option for an even more restricted service is -# DynamicUser=yes -# see http://0pointer.net/blog/dynamic-users-with-systemd.html -RuntimeDirectory=api -WorkingDirectory=/var/lib/api/halfapi -EnvironmentFile=/var/lib/api/halfapi/conf/env.merles-dev -ExecStart=/var/lib/api/.pyvenv/halfapi-MLzQW5Lp-py3.7/bin/uvicorn \ - --uds /var/lib/api/lirmm_api.sock \ - halfapi.app:app -ExecReload=/bin/kill -s HUP $MAINPID -KillMode=mixed -TimeoutStopSec=5 -PrivateTmp=true - -[Install] -WantedBy=multi-user.target diff --git a/halfapi/__init__.py b/halfapi/__init__.py index c57bfd5..0d793b3 100644 --- a/halfapi/__init__.py +++ b/halfapi/__init__.py @@ -1 +1,48 @@ -__version__ = '0.0.0' +#!/usr/bin/env python3 +import os +from os import environ +from configparser import ConfigParser + +__version__ = '0.1.0' +print(f'HalfAPI version:{__version__}') + +config = ConfigParser(defaults={ + 'project': { + 'host': '127.0.0.1', + 'port': '8000', + 'secret': None, + 'base_dir': None, + 'production': False + } +}) +config.read(filenames=['.halfapiconfig']) +PROJECT_NAME = config.get('project', 'name') + +CONF_DIR = environ.get('HALFAPI_CONF_DIR', '/etc/halfapi') + +config.read(filenames=[os.path.join( + CONF_DIR, + PROJECT_NAME +)]) + +HOST = config.get('project', 'host') +PORT = config.getint('project', 'port') + +DB_NAME = f'halfapi_{PROJECT_NAME}' +with open(config.get('project', 'secret')) as secret_file: + SECRET = secret_file.read() + +PRODUCTION = config.getboolean('project', 'production') +BASE_DIR = config.get('project', 'base_dir') + +# DB +from half_orm.model import Model +db = Model(DB_NAME) +Domain = db.get_relation_class('api.domain') +APIRouter = db.get_relation_class('api.router') +APIRoute = db.get_relation_class('api.route') +AclFunction = db.get_relation_class('api.acl_function') +Acl = db.get_relation_class('api.acl') +RouteACL = db.get_relation_class('api.view.acl') + +from halfapi.app import application diff --git a/halfapi/app.py b/halfapi/app.py index 181f022..af49f65 100644 --- a/halfapi/app.py +++ b/halfapi/app.py @@ -1,108 +1,22 @@ #!/usr/bin/env python3 -# builtins -import importlib -import sys -from os import environ - # asgi framework from starlette.applications import Starlette from starlette.authentication import UnauthenticatedUser from starlette.middleware import Middleware -from starlette.requests import Request from starlette.responses import Response, JSONResponse from starlette.routing import Route -from starlette.types import ASGIApp from starlette.middleware.authentication import AuthenticationMiddleware # typing from typing import Any, Awaitable, Callable, MutableMapping -RequestResponseEndpoint = Callable[ [Request], Awaitable[Response] ] - -# hop-generated classes -from .models.api.domain import Domain # module libraries -from .config import CONFIG -from .lib.jwt_middleware import JWTAuthenticationBackend -from .lib.acl_caller_middleware import AclCallerMiddleware +from halfapi import HOST, PORT, DB_NAME, SECRET, PRODUCTION -from .lib.responses import * +from halfapi.lib.jwt_middleware import JWTAuthenticationBackend - -def mount_domains(app: ASGIApp, domains: list): - """ Procedure to mount the registered domains on their prefixes - - Parameters: - - - app (ASGIApp): The Starlette instance - - domains (list): The domains to mount, retrieved from the database - with their attributes "version", "name" - - Returns: Nothing - """ - - for domain in domains: - if 'name' not in domain.keys() or 'version' not in domain.keys(): - continue - - # Retrieve domain app according to domain details - try: - print(f'Will import {domain["name"]}.app:app') - # @TODO 4-configuration - # Store domain-specific information in a configuration file - - domain_mod = importlib.import_module( - f'{domain["name"]}.app') - domain_app = domain_mod.app - except ModuleNotFoundError: - sys.stderr.write( - f'Could not find module *{domain["name"]}* in sys.path\n') - continue - except ImportError: - sys.stderr.write(f'Could not import *app* from *{domain}*') - continue - except Exception as e: - sys.stderr.write(f'Error in import *{domain["name"]}*\n') - print(e) - continue - - - # Alter the openapi_url so the /docs page doesn't try to get - # /openapi.json (@TODO : report the bug to FastAPI) - domain_app.openapi_url = '/api/{version}/{name}/openapi.json'.format(**domain) - - # Mount the domain app on the prefix - # e.g. : /v4/organigramme - try: - app.mount('/{version}/{name}'.format(**domain), app=domain_app) - except Exception as e: - print(f'Failed to mount *{domain}*\n') - - -def startup(): - # This function is called at the instanciation of *app* - global app - - # Mount the registered domains - try: - domains_list = [elt for elt in Domain().select()] - mount_domains(app, domains_list) - except Exception as e: - sys.stderr.write('Error in the *domains* retrieval\n') - raise e - -if not CONFIG['HALFORM_SECRET']: - try: - CONFIG['HALFORM_SECRET'] = open('/etc/half_orm/secret').read() - print('Missing HALFORM_SECRET variable from configuration, \ - read it from /etc/half_orm/secret') - except FileNotFoundError: - print('No HALFORM_SECRET variable set, and /etc/half_orm/secret \ - inaccessible.') - sys.exit(1) - except PermissionError: - print("You don't have the right to read /etc/half_orm/secret") - sys.exit(1) +from halfapi.lib.responses import * +from halfapi.lib.routes import get_routes debug_routes = [ @@ -113,22 +27,19 @@ debug_routes = [ else JSONResponse({'user':False})), Route('/payload', lambda request, *args, **kwargs: JSONResponse({'payload':str(request.payload)})) -] if CONFIG['DEBUG'] else [] +] if not PRODUCTION else [] - -app = Starlette( - debug=CONFIG['DEBUG'], - routes=debug_routes, +application = Starlette( + debug=not PRODUCTION, + routes=debug_routes + get_routes(), middleware=[ Middleware(AuthenticationMiddleware, - backend=JWTAuthenticationBackend(secret_key=CONFIG['HALFORM_SECRET'])), - Middleware(AclCallerMiddleware), + backend=JWTAuthenticationBackend(secret_key=SECRET)) ], exception_handlers={ 401: UnauthorizedResponse, 404: NotFoundResponse, 500: InternalServerErrorResponse, 501: NotImplementedResponse - }, - on_startup=[startup], + } ) diff --git a/halfapi/cli.py b/halfapi/cli.py index f4d89e8..6f46cdc 100755 --- a/halfapi/cli.py +++ b/halfapi/cli.py @@ -1,4 +1,12 @@ #!/usr/bin/env python3 +from halfapi import (PROJECT_NAME, HOST, PORT, + PRODUCTION, + BASE_DIR, + Domain, + APIRouter, + APIRoute, + AclFunction, + Acl) # builtins import click @@ -6,22 +14,10 @@ import uvicorn import os import sys import importlib +from pprint import pprint -# database -import psycopg2 - -# hop-generated classes -from apidb.api.version import Version -from apidb.api.domain import Domain -from apidb.api.route import Route -from apidb.api.acl_function import AclFunction -from apidb.api.acl import Acl - - -HALFORM_DSN='' -HALFORM_SECRET='' CONTEXT_SETTINGS={ - 'default_map':{'run': {'port': 8000}} + 'default_map':{'run': {}} } @click.group(invoke_without_command=True, context_settings=CONTEXT_SETTINGS) @@ -30,111 +26,53 @@ def cli(ctx): if ctx.invoked_subcommand is None: return run() -@click.option('--envfile', default=None) -@click.option('--host', default='127.0.0.1') -@click.option('--port', default='8000') -@cli.command() -def run(envfile, host, port): - local_env = {} - if envfile: - try: - with open(envfile) as f: - click.echo('Will use the following env parameters :') - local_env = dict([ tuple(line.strip().split('=', 1)) - for line in f.readlines() ]) - click.echo(local_env) - except FileNotFoundError: - click.echo(f'No file named {envfile}') - envfile = None - if 'DEV' in local_env.keys(): - debug = True - reload = True - log_level = 'debug' - else: - reload = False - log_level = 'info' +@click.option('--host', default=HOST) +@click.option('--port', default=PORT) +@cli.command() +def run(host, port): + debug = reload = not PRODUCTION + log_level = 'info' if PRODUCTION else 'debug' click.echo('Launching application') - sys.path.insert(0, os.getcwd()) + sys.path.insert(0, BASE_DIR) click.echo(f'current python_path : {sys.path}') uvicorn.run('halfapi.app:app', - env_file=envfile, host=host, port=int(port), log_level=log_level, reload=reload) -@click.option('--dbname', default='api') -@click.option('--host', default='127.0.0.1') -@click.option('--port', default=5432) -@click.option('--apihost', default='127.0.0.1') -@click.option('--apiport', default=8080) -@click.option('--user', default='api') -@click.option('--password', default='') -@click.option('--domain', default='organigramme') -@click.option('--drop', is_flag=True, default=False) + +def delete_domain(domain): + d = Domain(name=domain) + if len(d) != 1: + return False + + d.delete(delete_all=True) + return True + + +@click.option('--domain', default=None) @cli.command() -def dbupdate(dbname, host, port, apihost, apiport, user, password, domain, drop): - - def dropdb(): - if not click.confirm(f'will now drop database {dbname}', default=True): - return False - - conn = psycopg2.connect({ - 'dbname': dbname, - 'host': host, - 'port': port, - 'user': user, - 'password': password - }) - - cur = conn.cursor() - - cur.execute(f'drop database {dbname};') - conn.commit() - cur.close() - conn.close() - - return True - - def delete_domain(): - d = Domain(name=domain) - if len(d) < 1: - return False - - acl = Acl(domain=domain) - acl.delete() - - fct = AclFunction(domain=domain) - fct.delete() - - route = Route(domain=domain) - route.delete() - - d.delete() - - return True +def dbupdate(domain): def add_acl_fct(fct): acl = AclFunction() - acl.version = version acl.domain = domain acl.name = fct.__name__ if len(acl) == 0: acl.insert() - def add_acl(name, **kwargs): - acl = Acl() - acl.version = version - acl.domain = domain - acl.name = name - acl.path = kwargs['path'] - acl.http_verb = kwargs['verb'] - for fct in kwargs['acl']: - acl.function = fct.__name__ + + def add_acls(acls, **route): + route.pop('fct_name') + acl = Acl(**route) + + for fct in acls: + acl.acl_fct_name = fct.__name__ if len(acl) == 0: if fct is not None: @@ -146,38 +84,55 @@ def dbupdate(dbname, host, port, apihost, apiport, user, password, domain, drop) acl.delete() - def add_route(name, **kwargs): - click.echo(f'Adding route {version}/{domain}/{name}') - route = Route() - route.version = version + def get_fct_name(http_verb, path): + if path[0] != '/': + raise Exception('Malformed path') + + elts = path[1:].split('/') + + fct_name = [http_verb.lower()] + for elt in elts: + if elt[0] == '{': + fct_name.append(elt[1:-1].split(':')[0].upper()) + else: + fct_name.append(elt) + + return '_'.join(fct_name) + + + def add_router(name): + router = APIRouter() + router.name = name + router.domain = domain + + if len(router) == 0: + router.insert() + + + def add_route(http_verb, path, router, acls): + click.echo(f'Adding route /{domain}/{router}{path}') + route = APIRoute() + route.http_verb = http_verb + route.path = path + route.fct_name = get_fct_name(http_verb, path) + route.router = router route.domain = domain - route.path = kwargs['path'] + if len(route) == 0: route.insert() - - def add_routes_and_acl(routes): - for name, route_params in routes.items(): - add_route(name, **route_params) - add_acl(name, **route_params) + + add_acls(acls, **route.to_dict()) def add_domain(): - new_version = Version(name=version, server=apihost, port=apiport) - if len(new_version) == 0: - click.echo(f'New version : {version}') - new_version.insert() - new_domain = Domain(name=domain) - new_domain.version = version if len(new_domain) == 0: click.echo(f'New domain {domain}') new_domain.insert() + sys.path.insert(0, BASE_DIR) - if drop: - dropdb() - - delete_domain() + delete_domain(domain) acl_set = set() @@ -186,19 +141,20 @@ def dbupdate(dbname, host, port, apihost, apiport, user, password, domain, drop) # module retrieval dom_mod = importlib.import_module(domain) - version = dom_mod.API_VERSION add_domain() - # add main routes - ROUTES = dom_mod.ROUTES - add_routes_and_acl(dom_mod.ROUTES) - # add sub routers ROUTERS = dom_mod.ROUTERS for router_name in dom_mod.ROUTERS: router_mod = importlib.import_module(f'.routers.{router_name}', domain) - add_routes_and_acl(router_mod.ROUTES) + add_router(router_name) + + pprint(router_mod.ROUTES) + for route_path, route_params in router_mod.ROUTES.items(): + for http_verb, acls in route_params.items(): + add_route(http_verb, route_path, router_name, acls) + except ImportError: click.echo(f'The domain {domain} has no *ROUTES* variable', err=True) @@ -206,9 +162,5 @@ def dbupdate(dbname, host, port, apihost, apiport, user, password, domain, drop) click.echo(e, err=True) - - - if __name__ == '__main__': cli() - diff --git a/halfapi/config.py b/halfapi/config.py deleted file mode 100644 index 9ad2cf8..0000000 --- a/halfapi/config.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python3 -from os import environ - -# Configuration -CONFIG={} -CONFIG['DEBUG'] = environ.get('DEBUG', False) -CONFIG['DEBUG_ACL'] = environ.get('DEBUG_ACL', False) -CONFIG['HALFORM_SECRET'] = environ.get('HALFORM_SECRET', False) - - diff --git a/halfapi/lib/acl_caller_middleware.py b/halfapi/lib/acl_caller_middleware.py deleted file mode 100644 index 8d583f4..0000000 --- a/halfapi/lib/acl_caller_middleware.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env python3 -from os import environ - -from starlette.exceptions import HTTPException -from starlette.middleware.base import BaseHTTPMiddleware -from starlette.routing import Match, Mount -from starlette.types import ASGIApp, Receive, Scope, Send - -from halfapi.config import CONFIG -from halfapi.models.api.view.acl import Acl as AclView - -class DebugRouteException(Exception): - def __init__(self, *args, **kwargs): - super().__init__(self) - -def match_route(app: ASGIApp, scope: Scope): - """ Checks all routes from "app" and checks if it matches with the one from - scope - - Parameters: - - - app (ASGIApp): The Starlette instance - - scope (MutableMapping[str, Any]): The requests scope - - Returns: - - - (dict, dict): The first dict of the tuple is the details on the - route, the second one is the path parameters - - Raises: - - HTTPException - """ - - """ The *result* variable is fitted to the filter that will be applied when - searching the route in the database. - Refer to the database documentation for more details on the api.route - table. - """ - - result = { - 'domain': None, - 'name': None, - 'http_verb': None, - 'version': None - } - - if 'DEBUG' in CONFIG.keys() and len(scope['path'].split('/')) <= 3: - raise DebugRouteException() - - try: - """ Identification of the parts of the path - - Examples : - version : v4 - domain : organigramme - path : laboratoire/personnel - """ - _, result['domain'], path = scope['path'].split('/', 2) - except ValueError as e: - #404 Not found - raise HTTPException(404) - # Prefix the path with "/" - path = f'/{path}' - - for route in app.routes: - - if type(route) != Mount: - """ The root app should not have exposed routes, - only the mounted domains have some. - """ - continue - - """ Clone the scope to assign the path to the path without the - matching domain, be careful to the "root_path" of the mounted domain. - - @TODO - Also, improper array unpacking may make crash the program without any - explicit error, we may have to improve this as we only rely on this - function to accomplish all the routing - """ - subscope = scope.copy() - _, result['domain'], subpath = path.split('/', 2) - subscope['path'] = f'/{subpath}' - - for mount_route in route.routes: - # Parse all domain routes - submatch = mount_route.matches(subscope) - if submatch[0] != Match.FULL: - continue - - # Route matches - try: - result['name'] = submatch[1]['endpoint'].__name__ - result['http_verb'] = scope['method'] - except Exception as e: - print(e) - - return result, submatch[1]['path_params'] - - raise HTTPException(404) - - -class AclCallerMiddleware(BaseHTTPMiddleware): - async def __call__(self, scope:Scope, receive: Receive, send: Send) -> None: - """ Points out to the domain which ACL function it should call - - Parameters : - - - request (Request): The current request - - - call_next (RequestResponseEndpoint): The next middleware/route function - - Return: - Response - """ - print('Hit AclCallerMiddleware of API') - - if scope['type'] != 'http': - await self.app(scope, receive, send) - return - - app = self.app - while True: - if not hasattr(app, 'app'): - break - app = app.app - - if scope['path'].split('/')[-1] not in ['docs','openapi.json','redoc']: - # routes in the the database, the others being - # docs/openapi.json/redoc - - try: - d_match, path_params = match_route(app, scope) - scope['acls'] = [] - for acl in AclView(**d_match).select(): - # retrieve related ACLs - - if ('acl_function_name' not in acl.keys() - or 'domain' not in acl.keys()): - continue - - scope['acls'].append(acl['acl_function_name']) - - except StopIteration: - # TODO : No ACL sur une route existante, prevenir l'admin? - print("No ACL") - pass - except DebugRouteException: - print("Debug route") - if 'DEBUG_ACL' in environ.keys(): - scope['acls'] = environ['DEBUG_ACL'].split(':') - else: - scope['acls'] = [] - - elif CONFIG['DEBUG']: - scope['dev_route'] = True - - res = await self.app(scope, receive, send) diff --git a/halfapi/lib/acl_middleware.py b/halfapi/lib/acl_middleware.py deleted file mode 100644 index ae52057..0000000 --- a/halfapi/lib/acl_middleware.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python3 -from starlette.requests import Request -from starlette.exceptions import HTTPException -from starlette.middleware.base import BaseHTTPMiddleware - - -class AclMiddleware(BaseHTTPMiddleware): - def __init__(self, app, acl_module): - super().__init__(app) - self.acl_module = acl_module - async def dispatch(self, request: Request, call_next): - """ Checks the "acls" key in the scope and applies the - corresponding functions in the current module's acl lib. - - Raises an exception if no acl function returns True - """ - print(f'Hit acl {__name__} middleware') - - if 'dev_route' in request.scope.keys(): - print('[DEBUG] Dev route, no ACL') - return await call_next(request) - - if not('acls' in request.scope.keys() - and type(request.scope['acls']) == list): - - print('BUG : scope["acls"] does not exist or is not a list') - raise HTTPException(500) - - for acl_fct_name in request.scope['acls']: - print(f'Will apply {acl_fct_name}') - try: - fct = getattr(self.acl_module, acl_fct_name) - if fct(request) is True: - return await call_next(request) - - except AttributeError as e: - print(f'No ACL function "{acl_fct_name}" in {__name__} module') - print(e) - break - - except Exception as e: - print(e) - raise HTTPException(500) - - raise HTTPException(401) diff --git a/halfapi/lib/routes.py b/halfapi/lib/routes.py new file mode 100644 index 0000000..953ace4 --- /dev/null +++ b/halfapi/lib/routes.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +from functools import wraps +import importlib +import sys + +from halfapi import (PROJECT_NAME, HOST, PORT, + PRODUCTION, + Domain, + APIRouter, + APIRoute, + AclFunction, + Acl) +from halfapi.lib.responses import * +from starlette.exceptions import HTTPException +from starlette.routing import Mount, Route +from starlette.requests import Request + +def get_routes(domains=None): + """ Procedure to mount the registered domains on their prefixes + + Parameters: + + - app (ASGIApp): The Starlette instance + - domains (list): The domains to mount, retrieved from the database + with their attributes "name" + + Returns: Nothing + """ + + + def route_decorator(fct, acls_mod, acls): + @wraps(fct) + def caller(req: Request, *args, **kwargs): + for acl_fct_name in acls: + acl_fct = getattr(acls_mod, acl_fct_name) + if acl_fct(req, *args, **kwargs): + return func(req, *args, **kwargs) + + raise HTTPException(401) + + return caller + + app_routes = [] + for domain in Domain(name=domains).select(): + domain_acl_mod = importlib.import_module(f'{domain["name"]}.acl') + domain_routes = [] + for router in APIRouter(domain=domain['name']).select(): + router_routes = [] + + router_mod = importlib.import_module( + '{domain}.routers.{name}'.format(**router)) + + with APIRoute(domain=domain['name'], + router=router['name']) as routes: + for route in routes.select(): + fct_name = route.pop('fct_name') + acls = [ list(elt.values()).pop() + for elt in Acl(**route).select('acl_fct_name') ] + + router_routes.append( + Route(route['path'], + route_decorator( + getattr(router_mod, fct_name), + domain_acl_mod, + acls + ), methods=[route['http_verb']]) + ) + + domain_routes.append( + Mount('/{name}'.format(**router), routes=router_routes)) + + app_routes.append(Mount('/{name}'.format(**domain), + routes=domain_routes)) + return app_routes diff --git a/halfapi/models/__init__.py b/halfapi/models/__init__.py deleted file mode 100644 index 8aaa822..0000000 --- a/halfapi/models/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -"""This file is part of the apidb package. It has been generated by the -command halfORM. To keep it in sync with your database structure, just rerun -halfORM. - -More information on the half_orm library on https://github.com/collorg/halfORM. -""" - -__all__ = [ - 'api', - 'db_connector' -] diff --git a/halfapi/models/api.sql b/halfapi/models/api.sql index 5b89aaa..43176d5 100644 --- a/halfapi/models/api.sql +++ b/halfapi/models/api.sql @@ -2,72 +2,73 @@ create schema api; create type verb as enum ('POST', 'GET', 'PUT', 'DELETE'); -create table api.version ( - name text primary key, - server cidr not null default '127.0.0.1', - port integer not null +create table api.domain ( + name text, + primary key (name) ); -create table api.domain ( - version text references api.version(name), +create table api.router ( name text, - primary key (version, name) + domain text, + primary key (name, domain) ); +alter table api.router add constraint router_domain_fkey foreign key (domain) references api.domain(name) on update cascade on delete cascade; + create table api.route ( - path text, -- relative to /api// - version text, + http_verb verb, + path text, -- relative to // + fct_name text, + router text, domain text, - primary key (path, domain, version) + primary key (http_verb, path, router, domain) ); -alter table api.route add constraint route_domain_fkey foreign key (version, domain) references api.domain(version, name) on update cascade on delete cascade; +alter table api.route add constraint route_router_fkey foreign key (router, domain) references api.router(name, domain) on update cascade on delete cascade; create table api.acl_function ( name text, description text, - version text, domain text, - primary key (name, version, domain) + primary key (name, domain) ); -alter table api.acl_function add constraint acl_function_domain_fkey foreign key (version, domain) references api.domain(version, name) on update cascade on delete cascade; +alter table api.acl_function add constraint acl_function_domain_fkey foreign key (domain) references api.domain(name) on update cascade on delete cascade; create table api.acl ( - name text, http_verb verb, path text not null, - version text, - domain text not null, - function text not null, - primary key (name, version, domain, function) + router text, + domain text, + acl_fct_name text, + primary key (http_verb, path, router, domain, acl_fct_name) ); -alter table api.acl add constraint acl_route_fkey foreign key (path, version, domain) references api.route(path, version, domain) on update cascade on delete cascade; -alter table api.acl add constraint acl_function_fkey foreign key (function, version, domain) references api.acl_function(name, version, domain) on update cascade on delete cascade; +alter table api.acl add constraint acl_route_fkey foreign key (http_verb, path, +router, domain) references api.route(http_verb, path, router, domain) on update cascade on delete cascade; +alter table api.acl add constraint acl_function_fkey foreign key (acl_fct_name, domain) references api.acl_function(name, domain) on update cascade on delete cascade; create schema "api.view"; create view "api.view".route as select route.*, - version.name, - version.server, - version.port, - '/'::text || route.domain || route.path AS abs_path + '/'::text || route.domain || '/'::text || route.router || route.path AS abs_path from api.route join api.domain on route.domain = domain.name - join api.version on - domain.version = version.name; +; create view "api.view".acl as select acl.*, - acl_function.name as acl_function_name, - '/'::text || acl.domain || acl.path AS abs_path + '/'::text || route.domain || '/'::text || route.router || route.path AS abs_path from api.acl join api.acl_function on - acl.function = acl_function.name; + acl.acl_fct_name = acl_function.name + join api.route on + acl.domain = route.domain + and acl.router = route.router + and acl.path = route.path; diff --git a/halfapi/models/api/__init__.py b/halfapi/models/api/__init__.py deleted file mode 100644 index 3fe4bd2..0000000 --- a/halfapi/models/api/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -"""This file is part of the apidb package. It has been generated by the -command halfORM. To keep it in sync with your database structure, just rerun -halfORM. - -More information on the half_orm library on https://github.com/collorg/halfORM. -""" - -__all__ = [ - 'acl', - 'acl_function', - 'domain', - 'route', - 'version', - 'view' -] diff --git a/halfapi/models/api/acl.py b/halfapi/models/api/acl.py deleted file mode 100644 index e065fc4..0000000 --- a/halfapi/models/api/acl.py +++ /dev/null @@ -1,52 +0,0 @@ -#-*- coding: utf-8 -*- -# pylint: disable=wrong-import-order - -"""The module apidb.api.acl povides the Acl class. - -WARNING! - -This file is part of the apidb package. It has been generated by the -command halfORM. To keep it in sync with your database structure, just rerun -halfORM. - -More information on the half_orm library on https://github.com/collorg/halfORM. - - -DO NOT REMOVE OR MODIFY THE LINES BEGINING WITH: -#>>> PLACE YOUR CODE BELOW... -#<<< PLACE YOUR CODE ABOVE... - -MAKE SURE YOU PLACE YOUR CODE BETWEEN THESE LINES OR AT THE END OF THE FILE. -halfORM ONLY PRESERVES THE CODE BETWEEN THESE MARKS WHEN IT IS RUN. -""" - -from ..db_connector import base_relation_class - -#>>> PLACE YOUR CODE BELLOW THIS LINE. DO NOT REMOVE THIS LINE! - -#<<< PLACE YOUR CODE ABOVE THIS LINE. DO NOT REMOVE THIS LINE! - -__RCLS = base_relation_class('api.acl') - -class Acl( __RCLS): - """ - __RCLS: - This class allows you to manipulate the data in the PG relation: - TABLE: "api"."api"."acl" - FIELDS: - - name: (text) PK - - http_verb: (verb) - - path: (text) NOT NULL - - version: (text) PK - - domain: (text) PK - - function: (text) PK - FOREIGN KEYS: - - acl_route_fkey: (path, version, domain) - ↳ "api"."api"."route"(path, version, domain) - - acl_function_fkey: (function, version, domain) - ↳ "api"."api"."acl_function"(name, version, domain) - """ - def __init__(self, **kwargs): - super(Acl, self).__init__(**kwargs) - - #>>> PLACE YOUR CODE BELLOW THIS LINE. DO NOT REMOVE THIS LINE! diff --git a/halfapi/models/api/acl_function.py b/halfapi/models/api/acl_function.py deleted file mode 100644 index 2aecf9d..0000000 --- a/halfapi/models/api/acl_function.py +++ /dev/null @@ -1,50 +0,0 @@ -#-*- coding: utf-8 -*- -# pylint: disable=wrong-import-order - -"""The module apidb.api.acl_function povides the AclFunction class. - -WARNING! - -This file is part of the apidb package. It has been generated by the -command halfORM. To keep it in sync with your database structure, just rerun -halfORM. - -More information on the half_orm library on https://github.com/collorg/halfORM. - - -DO NOT REMOVE OR MODIFY THE LINES BEGINING WITH: -#>>> PLACE YOUR CODE BELOW... -#<<< PLACE YOUR CODE ABOVE... - -MAKE SURE YOU PLACE YOUR CODE BETWEEN THESE LINES OR AT THE END OF THE FILE. -halfORM ONLY PRESERVES THE CODE BETWEEN THESE MARKS WHEN IT IS RUN. -""" - -from ..db_connector import base_relation_class - -#>>> PLACE YOUR CODE BELLOW THIS LINE. DO NOT REMOVE THIS LINE! - -#<<< PLACE YOUR CODE ABOVE THIS LINE. DO NOT REMOVE THIS LINE! - -__RCLS = base_relation_class('api.acl_function') - -class AclFunction( __RCLS): - """ - __RCLS: - This class allows you to manipulate the data in the PG relation: - TABLE: "api"."api"."acl_function" - FIELDS: - - name: (text) PK - - description: (text) - - version: (text) PK - - domain: (text) PK - FOREIGN KEYS: - - _reverse_fkey_api_api_acl_function_version_domain: (name, version, domain) - ↳ "api"."api"."acl"(function, version, domain) - - acl_function_domain_fkey: (version, domain) - ↳ "api"."api"."domain"(version, name) - """ - def __init__(self, **kwargs): - super(AclFunction, self).__init__(**kwargs) - - #>>> PLACE YOUR CODE BELLOW THIS LINE. DO NOT REMOVE THIS LINE! diff --git a/halfapi/models/api/domain.py b/halfapi/models/api/domain.py deleted file mode 100644 index 5896fc5..0000000 --- a/halfapi/models/api/domain.py +++ /dev/null @@ -1,50 +0,0 @@ -#-*- coding: utf-8 -*- -# pylint: disable=wrong-import-order - -"""The module apidb.api.domain povides the Domain class. - -WARNING! - -This file is part of the apidb package. It has been generated by the -command halfORM. To keep it in sync with your database structure, just rerun -halfORM. - -More information on the half_orm library on https://github.com/collorg/halfORM. - - -DO NOT REMOVE OR MODIFY THE LINES BEGINING WITH: -#>>> PLACE YOUR CODE BELOW... -#<<< PLACE YOUR CODE ABOVE... - -MAKE SURE YOU PLACE YOUR CODE BETWEEN THESE LINES OR AT THE END OF THE FILE. -halfORM ONLY PRESERVES THE CODE BETWEEN THESE MARKS WHEN IT IS RUN. -""" - -from ..db_connector import base_relation_class - -#>>> PLACE YOUR CODE BELLOW THIS LINE. DO NOT REMOVE THIS LINE! - -#<<< PLACE YOUR CODE ABOVE THIS LINE. DO NOT REMOVE THIS LINE! - -__RCLS = base_relation_class('api.domain') - -class Domain( __RCLS): - """ - __RCLS: - This class allows you to manipulate the data in the PG relation: - TABLE: "api"."api"."domain" - FIELDS: - - version: (text) PK - - name: (text) PK - FOREIGN KEYS: - - _reverse_fkey_api_api_acl_function_version_domain: (version, name) - ↳ "api"."api"."acl_function"(version, domain) - - domain_version_fkey: (version) - ↳ "api"."api"."version"(name) - - _reverse_fkey_api_api_route_version_domain: (version, name) - ↳ "api"."api"."route"(version, domain) - """ - def __init__(self, **kwargs): - super(Domain, self).__init__(**kwargs) - - #>>> PLACE YOUR CODE BELLOW THIS LINE. DO NOT REMOVE THIS LINE! diff --git a/halfapi/models/api/route.py b/halfapi/models/api/route.py deleted file mode 100644 index 596f8f7..0000000 --- a/halfapi/models/api/route.py +++ /dev/null @@ -1,49 +0,0 @@ -#-*- coding: utf-8 -*- -# pylint: disable=wrong-import-order - -"""The module apidb.api.route povides the Route class. - -WARNING! - -This file is part of the apidb package. It has been generated by the -command halfORM. To keep it in sync with your database structure, just rerun -halfORM. - -More information on the half_orm library on https://github.com/collorg/halfORM. - - -DO NOT REMOVE OR MODIFY THE LINES BEGINING WITH: -#>>> PLACE YOUR CODE BELOW... -#<<< PLACE YOUR CODE ABOVE... - -MAKE SURE YOU PLACE YOUR CODE BETWEEN THESE LINES OR AT THE END OF THE FILE. -halfORM ONLY PRESERVES THE CODE BETWEEN THESE MARKS WHEN IT IS RUN. -""" - -from ..db_connector import base_relation_class - -#>>> PLACE YOUR CODE BELLOW THIS LINE. DO NOT REMOVE THIS LINE! - -#<<< PLACE YOUR CODE ABOVE THIS LINE. DO NOT REMOVE THIS LINE! - -__RCLS = base_relation_class('api.route') - -class Route( __RCLS): - """ - __RCLS: - This class allows you to manipulate the data in the PG relation: - TABLE: "api"."api"."route" - FIELDS: - - path: (text) PK - - version: (text) PK - - domain: (text) PK - FOREIGN KEYS: - - _reverse_fkey_api_api_acl_path_version_domain: (path, version, domain) - ↳ "api"."api"."acl"(path, version, domain) - - route_domain_fkey: (version, domain) - ↳ "api"."api"."domain"(version, name) - """ - def __init__(self, **kwargs): - super(Route, self).__init__(**kwargs) - - #>>> PLACE YOUR CODE BELLOW THIS LINE. DO NOT REMOVE THIS LINE! diff --git a/halfapi/models/api/version.py b/halfapi/models/api/version.py deleted file mode 100644 index b1a6077..0000000 --- a/halfapi/models/api/version.py +++ /dev/null @@ -1,47 +0,0 @@ -#-*- coding: utf-8 -*- -# pylint: disable=wrong-import-order - -"""The module apidb.api.version povides the Version class. - -WARNING! - -This file is part of the apidb package. It has been generated by the -command halfORM. To keep it in sync with your database structure, just rerun -halfORM. - -More information on the half_orm library on https://github.com/collorg/halfORM. - - -DO NOT REMOVE OR MODIFY THE LINES BEGINING WITH: -#>>> PLACE YOUR CODE BELOW... -#<<< PLACE YOUR CODE ABOVE... - -MAKE SURE YOU PLACE YOUR CODE BETWEEN THESE LINES OR AT THE END OF THE FILE. -halfORM ONLY PRESERVES THE CODE BETWEEN THESE MARKS WHEN IT IS RUN. -""" - -from ..db_connector import base_relation_class - -#>>> PLACE YOUR CODE BELLOW THIS LINE. DO NOT REMOVE THIS LINE! - -#<<< PLACE YOUR CODE ABOVE THIS LINE. DO NOT REMOVE THIS LINE! - -__RCLS = base_relation_class('api.version') - -class Version( __RCLS): - """ - __RCLS: - This class allows you to manipulate the data in the PG relation: - TABLE: "api"."api"."version" - FIELDS: - - name: (text) PK - - server: (cidr) NOT NULL - - port: (int4) NOT NULL - FOREIGN KEY: - - _reverse_fkey_api_api_domain_version: (name) - ↳ "api"."api"."domain"(version) - """ - def __init__(self, **kwargs): - super(Version, self).__init__(**kwargs) - - #>>> PLACE YOUR CODE BELLOW THIS LINE. DO NOT REMOVE THIS LINE! diff --git a/halfapi/models/api/view/__init__.py b/halfapi/models/api/view/__init__.py deleted file mode 100644 index 8a38f03..0000000 --- a/halfapi/models/api/view/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -"""This file is part of the apidb package. It has been generated by the -command halfORM. To keep it in sync with your database structure, just rerun -halfORM. - -More information on the half_orm library on https://github.com/collorg/halfORM. -""" - -__all__ = [ - 'acl', - 'route' -] diff --git a/halfapi/models/api/view/acl.py b/halfapi/models/api/view/acl.py deleted file mode 100644 index 6fced55..0000000 --- a/halfapi/models/api/view/acl.py +++ /dev/null @@ -1,49 +0,0 @@ -#-*- coding: utf-8 -*- -# pylint: disable=wrong-import-order - -"""The module apidb.api.view.acl povides the Acl class. - -WARNING! - -This file is part of the apidb package. It has been generated by the -command halfORM. To keep it in sync with your database structure, just rerun -halfORM. - -More information on the half_orm library on https://github.com/collorg/halfORM. - - -DO NOT REMOVE OR MODIFY THE LINES BEGINING WITH: -#>>> PLACE YOUR CODE BELOW... -#<<< PLACE YOUR CODE ABOVE... - -MAKE SURE YOU PLACE YOUR CODE BETWEEN THESE LINES OR AT THE END OF THE FILE. -halfORM ONLY PRESERVES THE CODE BETWEEN THESE MARKS WHEN IT IS RUN. -""" - -from ...db_connector import base_relation_class - -#>>> PLACE YOUR CODE BELLOW THIS LINE. DO NOT REMOVE THIS LINE! - -#<<< PLACE YOUR CODE ABOVE THIS LINE. DO NOT REMOVE THIS LINE! - -__RCLS = base_relation_class('api.view.acl') - -class Acl( __RCLS): - """ - __RCLS: - This class allows you to manipulate the data in the PG relation: - VIEW: "api"."api.view"."acl" - FIELDS: - - name: (text) - - http_verb: (verb) - - path: (text) - - version: (text) - - domain: (text) - - function: (text) - - acl_function_name: (text) - - abs_path: (text) - """ - def __init__(self, **kwargs): - super(Acl, self).__init__(**kwargs) - - #>>> PLACE YOUR CODE BELLOW THIS LINE. DO NOT REMOVE THIS LINE! diff --git a/halfapi/models/api/view/route.py b/halfapi/models/api/view/route.py deleted file mode 100644 index f8d9eab..0000000 --- a/halfapi/models/api/view/route.py +++ /dev/null @@ -1,48 +0,0 @@ -#-*- coding: utf-8 -*- -# pylint: disable=wrong-import-order - -"""The module apidb.api.view.route povides the Route class. - -WARNING! - -This file is part of the apidb package. It has been generated by the -command halfORM. To keep it in sync with your database structure, just rerun -halfORM. - -More information on the half_orm library on https://github.com/collorg/halfORM. - - -DO NOT REMOVE OR MODIFY THE LINES BEGINING WITH: -#>>> PLACE YOUR CODE BELOW... -#<<< PLACE YOUR CODE ABOVE... - -MAKE SURE YOU PLACE YOUR CODE BETWEEN THESE LINES OR AT THE END OF THE FILE. -halfORM ONLY PRESERVES THE CODE BETWEEN THESE MARKS WHEN IT IS RUN. -""" - -from ...db_connector import base_relation_class - -#>>> PLACE YOUR CODE BELLOW THIS LINE. DO NOT REMOVE THIS LINE! - -#<<< PLACE YOUR CODE ABOVE THIS LINE. DO NOT REMOVE THIS LINE! - -__RCLS = base_relation_class('api.view.route') - -class Route( __RCLS): - """ - __RCLS: - This class allows you to manipulate the data in the PG relation: - VIEW: "api"."api.view"."route" - FIELDS: - - path: (text) - - version: (text) - - domain: (text) - - name: (text) - - server: (cidr) - - port: (int4) - - abs_path: (text) - """ - def __init__(self, **kwargs): - super(Route, self).__init__(**kwargs) - - #>>> PLACE YOUR CODE BELLOW THIS LINE. DO NOT REMOVE THIS LINE! diff --git a/halfapi/models/db_connector.py b/halfapi/models/db_connector.py deleted file mode 100644 index b0d2673..0000000 --- a/halfapi/models/db_connector.py +++ /dev/null @@ -1,17 +0,0 @@ -#-*- coding: utf-8 -*- - -"""This module exports the fonction base_relation_class which is -imported by all modules in the package apidb. -""" - -from half_orm.model import Model - -__all__ = ['base_relation_class'] - -MODEL = Model('api', scope=__name__) - -def base_relation_class(qrn): - """Returns the class corresponding to the QRN (qualified relation name). - """ - cls = MODEL.get_relation_class(qrn) - return cls diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index a153d8e..0000000 --- a/poetry.lock +++ /dev/null @@ -1,848 +0,0 @@ -[[package]] -category = "main" -description = "Package for api PG (apidb)" -name = "apidb" -optional = false -python-versions = "*" -version = "0.0.6" - -[package.dependencies] -half_orm = "*" - -[package.source] -reference = "616cef51b8aa5b5c07a05c9c47c9a6ae9656ffa1" -type = "git" -url = "git@gite.lirmm.fr:newsi/db/hop_api.git" -[[package]] -category = "dev" -description = "Atomic file writes." -marker = "sys_platform == \"win32\"" -name = "atomicwrites" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.4.0" - -[[package]] -category = "dev" -description = "Classes Without Boilerplate" -name = "attrs" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "19.3.0" - -[package.extras] -azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"] -dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"] -docs = ["sphinx", "zope.interface"] -tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] - -[[package]] -category = "dev" -description = "Python package for providing Mozilla's CA Bundle." -name = "certifi" -optional = false -python-versions = "*" -version = "2020.6.20" - -[[package]] -category = "dev" -description = "Universal encoding detector for Python 2 and 3" -name = "chardet" -optional = false -python-versions = "*" -version = "3.0.4" - -[[package]] -category = "main" -description = "Composable command line interface toolkit" -name = "click" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "7.1.2" - -[[package]] -category = "dev" -description = "Cross-platform colored terminal text." -marker = "sys_platform == \"win32\"" -name = "colorama" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.4.3" - -[[package]] -category = "main" -description = "XML bomb protection for Python stdlib modules" -name = "defusedxml" -optional = true -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.6.0" - -[[package]] -category = "main" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -name = "fastapi" -optional = true -python-versions = ">=3.6" -version = "0.59.0" - -[package.dependencies] -pydantic = ">=0.32.2,<2.0.0" -starlette = "0.13.4" - -[package.extras] -all = ["requests (>=2.24.0,<3.0.0)", "aiofiles (>=0.5.0,<0.6.0)", "jinja2 (>=2.11.2,<3.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<2.0.0)", "pyyaml (>=5.3.1,<6.0.0)", "graphene (>=2.1.8,<3.0.0)", "ujson (>=3.0.0,<4.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn (>=0.11.5,<0.12.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)"] -dev = ["python-jose (>=3.1.0,<4.0.0)", "passlib (>=1.7.2,<2.0.0)", "autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn (>=0.11.5,<0.12.0)", "graphene (>=2.1.8,<3.0.0)"] -doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=5.4.0,<6.0.0)", "markdown-include (>=0.5.1,<0.6.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.2.0)", "typer (>=0.3.0,<0.4.0)", "typer-cli (>=0.0.9,<0.0.10)", "pyyaml (>=5.3.1,<6.0.0)"] -test = ["pytest (5.4.3)", "pytest-cov (2.10.0)", "mypy (0.782)", "black (19.10b0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "databases (>=0.3.2,<0.4.0)", "orjson (>=3.2.1,<4.0.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "aiofiles (>=0.5.0,<0.6.0)", "flask (>=1.1.2,<2.0.0)"] - -[[package]] -category = "main" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -name = "h11" -optional = false -python-versions = "*" -version = "0.9.0" - -[[package]] -category = "main" -description = "A simple ORM in Python only dealing with the DML part of SQL." -name = "half-orm" -optional = false -python-versions = "*" -version = "0.2.0" - -[package.dependencies] -PyYAML = "*" -psycopg2-binary = "*" - -[package.source] -reference = "fe53195abb637d2192857e8b4878f4865b0fcce4" -type = "git" -url = "git@gite.lirmm.fr:newsi/halfORM.git" -[[package]] -category = "main" -description = "A collection of framework independent HTTP protocol utils." -marker = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"" -name = "httptools" -optional = false -python-versions = "*" -version = "0.1.1" - -[package.extras] -test = ["Cython (0.29.14)"] - -[[package]] -category = "dev" -description = "Internationalized Domain Names in Applications (IDNA)" -name = "idna" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.10" - -[[package]] -category = "dev" -description = "Read metadata from Python packages" -marker = "python_version < \"3.8\"" -name = "importlib-metadata" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -version = "1.7.0" - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["sphinx", "rst.linker"] -testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] - -[[package]] -category = "main" -description = "Load me later. A lazy plugin management system." -name = "lml" -optional = true -python-versions = "*" -version = "0.0.9" - -[[package]] -category = "main" -description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -name = "lxml" -optional = true -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" -version = "4.5.2" - -[package.extras] -cssselect = ["cssselect (>=0.7)"] -html5 = ["html5lib"] -htmlsoup = ["beautifulsoup4"] -source = ["Cython (>=0.29.7)"] - -[[package]] -category = "dev" -description = "More routines for operating on iterables, beyond itertools" -name = "more-itertools" -optional = false -python-versions = ">=3.5" -version = "8.4.0" - -[[package]] -category = "main" -description = "Python API and tools to manipulate OpenDocument files" -name = "odfpy" -optional = true -python-versions = "*" -version = "1.4.1" - -[package.dependencies] -defusedxml = "*" - -[[package]] -category = "main" -description = "" -name = "organigramme" -optional = true -python-versions = "^3.7" -version = "0.1.0" - -[package.dependencies] -fastapi = "^0" -half-orm = "branch master" -halfapi = "branch master" -pyexcel = "^0.6.2" -pyexcel-ods = "^0.5.6" -pyexcel-ods3 = "^0.5.3" -python-dotenv = "^0.14.0" -sidb = "branch master" -starlette = "^0" -uvicorn = "^0" - -[package.source] -reference = "41984a0f62ee0a951ae06647601dd4a7091c5299" -type = "git" -url = "git@gite.lirmm.fr:newsi/api/organigramme.git" -[[package]] -category = "dev" -description = "Core utilities for Python packages" -name = "packaging" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "20.4" - -[package.dependencies] -pyparsing = ">=2.0.2" -six = "*" - -[[package]] -category = "dev" -description = "plugin and hook calling mechanisms for python" -name = "pluggy" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.13.1" - -[package.dependencies] -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=0.12" - -[package.extras] -dev = ["pre-commit", "tox"] - -[[package]] -category = "main" -description = "psycopg2 - Python-PostgreSQL Database Adapter" -name = "psycopg2-binary" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "2.8.5" - -[[package]] -category = "dev" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -name = "py" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.9.0" - -[[package]] -category = "main" -description = "Data validation and settings management using python 3.6 type hinting" -name = "pydantic" -optional = true -python-versions = ">=3.6" -version = "1.6.1" - -[package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] -typing_extensions = ["typing-extensions (>=3.7.2)"] - -[[package]] -category = "main" -description = "A wrapper library that provides one API to read, manipulate and writedata in different excel formats" -name = "pyexcel" -optional = true -python-versions = ">=3.6" -version = "0.6.2" - -[package.dependencies] -lml = ">=0.0.4" -pyexcel-io = ">=0.5.19" -texttable = ">=0.8.2" - -[package.extras] -ods = ["pyexcel-ods3 (>=0.5.0)"] -xls = ["pyexcel-xls (>=0.5.0)"] -xlsx = ["pyexcel-xlsx (>=0.5.0)"] - -[[package]] -category = "main" -description = "A Python package to create/manipulate OpenDocumentFormat files" -name = "pyexcel-ezodf" -optional = true -python-versions = "*" -version = "0.3.4" - -[package.dependencies] -lxml = "*" - -[[package]] -category = "main" -description = "A python library to read and write structured data in csv, zipped csvformat and to/from databases" -name = "pyexcel-io" -optional = true -python-versions = "*" -version = "0.5.20" - -[package.dependencies] -lml = ">=0.0.4" - -[package.extras] -ods = ["pyexcel-ods3 (>=0.5.0)"] -xls = ["pyexcel-xls (>=0.5.0)"] -xlsx = ["pyexcel-xlsx (>=0.5.0)"] - -[[package]] -category = "main" -description = "A wrapper library to read, manipulate and write data in ods format" -name = "pyexcel-ods" -optional = true -python-versions = "*" -version = "0.5.6" - -[package.dependencies] -odfpy = ">=1.3.5" -pyexcel-io = ">=0.5.16" - -[[package]] -category = "main" -description = "A wrapper library to read, manipulate and write data in ods format" -name = "pyexcel-ods3" -optional = true -python-versions = "*" -version = "0.5.3" - -[package.dependencies] -lxml = "*" -pyexcel-ezodf = ">=0.3.3" -pyexcel-io = ">=0.5.10" - -[[package]] -category = "main" -description = "JSON Web Token implementation in Python" -name = "pyjwt" -optional = false -python-versions = "*" -version = "1.7.1" - -[package.extras] -crypto = ["cryptography (>=1.4)"] -flake8 = ["flake8", "flake8-import-order", "pep8-naming"] -test = ["pytest (>=4.0.1,<5.0.0)", "pytest-cov (>=2.6.0,<3.0.0)", "pytest-runner (>=4.2,<5.0.0)"] - -[[package]] -category = "dev" -description = "Python parsing module" -name = "pyparsing" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "2.4.7" - -[[package]] -category = "dev" -description = "pytest: simple powerful testing with Python" -name = "pytest" -optional = false -python-versions = ">=3.5" -version = "5.4.3" - -[package.dependencies] -atomicwrites = ">=1.0" -attrs = ">=17.4.0" -colorama = "*" -more-itertools = ">=4.0.0" -packaging = "*" -pluggy = ">=0.12,<1.0" -py = ">=1.5.0" -wcwidth = "*" - -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=0.12" - -[package.extras] -checkqa-mypy = ["mypy (v0.761)"] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] - -[[package]] -category = "dev" -description = "py.test plugin that allows you to add environment variables." -name = "pytest-env" -optional = false -python-versions = "*" -version = "0.6.2" - -[package.dependencies] -pytest = ">=2.6.0" - -[[package]] -category = "main" -description = "Add .env support to your django/flask apps in development and deployments" -name = "python-dotenv" -optional = false -python-versions = "*" -version = "0.14.0" - -[package.extras] -cli = ["click (>=5.0)"] - -[[package]] -category = "main" -description = "YAML parser and emitter for Python" -name = "pyyaml" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "5.3.1" - -[[package]] -category = "dev" -description = "Python HTTP for Humans." -name = "requests" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.24.0" - -[package.dependencies] -certifi = ">=2017.4.17" -chardet = ">=3.0.2,<4" -idna = ">=2.5,<3" -urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" - -[package.extras] -security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] - -[[package]] -category = "main" -description = "Package for si PG" -name = "sidb" -optional = true -python-versions = "*" -version = "0.0.0" - -[package.dependencies] -half_orm = "*" - -[package.source] -reference = "d0f14a9631eecd29098d13a3f34e9cd533145f24" -type = "git" -url = "git@gite.lirmm.fr:newsi/sidb_halfORM.git" -[[package]] -category = "dev" -description = "Python 2 and 3 compatibility utilities" -name = "six" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -version = "1.15.0" - -[[package]] -category = "main" -description = "The little ASGI library that shines." -name = "starlette" -optional = false -python-versions = ">=3.6" -version = "0.13.4" - -[package.extras] -full = ["aiofiles", "graphene", "itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests", "ujson"] - -[[package]] -category = "main" -description = "module for creating simple ASCII tables" -name = "texttable" -optional = true -python-versions = "*" -version = "1.6.2" - -[[package]] -category = "dev" -description = "HTTP library with thread-safe connection pooling, file post, and more." -name = "urllib3" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" -version = "1.25.9" - -[package.extras] -brotli = ["brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] - -[[package]] -category = "main" -description = "The lightning-fast ASGI server." -name = "uvicorn" -optional = false -python-versions = "*" -version = "0.11.6" - -[package.dependencies] -click = ">=7.0.0,<8.0.0" -h11 = ">=0.8,<0.10" -httptools = ">=0.1.0,<0.2.0" -uvloop = ">=0.14.0" -websockets = ">=8.0.0,<9.0.0" - -[package.extras] -watchgodreload = ["watchgod (>=0.6,<0.7)"] - -[[package]] -category = "main" -description = "Fast implementation of asyncio event loop on top of libuv" -marker = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"" -name = "uvloop" -optional = false -python-versions = "*" -version = "0.14.0" - -[[package]] -category = "dev" -description = "Measures the displayed width of unicode strings in a terminal" -name = "wcwidth" -optional = false -python-versions = "*" -version = "0.2.5" - -[[package]] -category = "main" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -name = "websockets" -optional = false -python-versions = ">=3.6.1" -version = "8.1" - -[[package]] -category = "dev" -description = "Backport of pathlib-compatible object wrapper for zip files" -marker = "python_version < \"3.8\"" -name = "zipp" -optional = false -python-versions = ">=3.6" -version = "3.1.0" - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["jaraco.itertools", "func-timeout"] - -[extras] -organigramme = ["fastapi", "organigramme", "sidb", "pyexcel-ods3"] - -[metadata] -content-hash = "826725e53dedda2a3acbf7f004594358246da3c67c5006586c67e408fb41bee7" -python-versions = "^3.7" - -[metadata.files] -apidb = [] -atomicwrites = [ - {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, - {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, -] -attrs = [ - {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, - {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, -] -certifi = [ - {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"}, - {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"}, -] -chardet = [ - {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, - {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, -] -click = [ - {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, - {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, -] -colorama = [ - {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, - {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, -] -defusedxml = [ - {file = "defusedxml-0.6.0-py2.py3-none-any.whl", hash = "sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93"}, - {file = "defusedxml-0.6.0.tar.gz", hash = "sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"}, -] -fastapi = [ - {file = "fastapi-0.59.0-py3-none-any.whl", hash = "sha256:50b58aa3e7d5bcb4a4404ac7e550cc53f0cf7ca0fd13c7fd515693dc23c9caef"}, - {file = "fastapi-0.59.0.tar.gz", hash = "sha256:c04dacd3deed0fd0ffdcdb116b5a04ffa257656885be7fae7234f4f62ec4a0a9"}, -] -h11 = [ - {file = "h11-0.9.0-py2.py3-none-any.whl", hash = "sha256:4bc6d6a1238b7615b266ada57e0618568066f57dd6fa967d1290ec9309b2f2f1"}, - {file = "h11-0.9.0.tar.gz", hash = "sha256:33d4bca7be0fa039f4e84d50ab00531047e53d6ee8ffbc83501ea602c169cae1"}, -] -half-orm = [] -httptools = [ - {file = "httptools-0.1.1-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:a2719e1d7a84bb131c4f1e0cb79705034b48de6ae486eb5297a139d6a3296dce"}, - {file = "httptools-0.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:fa3cd71e31436911a44620473e873a256851e1f53dee56669dae403ba41756a4"}, - {file = "httptools-0.1.1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:86c6acd66765a934e8730bf0e9dfaac6fdcf2a4334212bd4a0a1c78f16475ca6"}, - {file = "httptools-0.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bc3114b9edbca5a1eb7ae7db698c669eb53eb8afbbebdde116c174925260849c"}, - {file = "httptools-0.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:ac0aa11e99454b6a66989aa2d44bca41d4e0f968e395a0a8f164b401fefe359a"}, - {file = "httptools-0.1.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:96da81e1992be8ac2fd5597bf0283d832287e20cb3cfde8996d2b00356d4e17f"}, - {file = "httptools-0.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:56b6393c6ac7abe632f2294da53f30d279130a92e8ae39d8d14ee2e1b05ad1f2"}, - {file = "httptools-0.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:96eb359252aeed57ea5c7b3d79839aaa0382c9d3149f7d24dd7172b1bcecb009"}, - {file = "httptools-0.1.1-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:fea04e126014169384dee76a153d4573d90d0cbd1d12185da089f73c78390437"}, - {file = "httptools-0.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:3592e854424ec94bd17dc3e0c96a64e459ec4147e6d53c0a42d0ebcef9cb9c5d"}, - {file = "httptools-0.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:0a4b1b2012b28e68306575ad14ad5e9120b34fccd02a81eb08838d7e3bbb48be"}, - {file = "httptools-0.1.1.tar.gz", hash = "sha256:41b573cf33f64a8f8f3400d0a7faf48e1888582b6f6e02b82b9bd4f0bf7497ce"}, -] -idna = [ - {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, - {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, -] -importlib-metadata = [ - {file = "importlib_metadata-1.7.0-py2.py3-none-any.whl", hash = "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"}, - {file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"}, -] -lml = [ - {file = "lml-0.0.9-py2.py3-none-any.whl", hash = "sha256:b1bef669dc077a1075fa64b99229b6341085b3b3a98d29c66df1853cc14e6c1a"}, - {file = "lml-0.0.9.tar.gz", hash = "sha256:ea5ba817b4adc9e9f5c21725cd2475f912933b7e2dfdf0792aed80077154f63f"}, -] -lxml = [ - {file = "lxml-4.5.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:74f48ec98430e06c1fa8949b49ebdd8d27ceb9df8d3d1c92e1fdc2773f003f20"}, - {file = "lxml-4.5.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e70d4e467e243455492f5de463b72151cc400710ac03a0678206a5f27e79ddef"}, - {file = "lxml-4.5.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:7ad7906e098ccd30d8f7068030a0b16668ab8aa5cda6fcd5146d8d20cbaa71b5"}, - {file = "lxml-4.5.2-cp27-cp27m-win32.whl", hash = "sha256:92282c83547a9add85ad658143c76a64a8d339028926d7dc1998ca029c88ea6a"}, - {file = "lxml-4.5.2-cp27-cp27m-win_amd64.whl", hash = "sha256:05a444b207901a68a6526948c7cc8f9fe6d6f24c70781488e32fd74ff5996e3f"}, - {file = "lxml-4.5.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:94150231f1e90c9595ccc80d7d2006c61f90a5995db82bccbca7944fd457f0f6"}, - {file = "lxml-4.5.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bea760a63ce9bba566c23f726d72b3c0250e2fa2569909e2d83cda1534c79443"}, - {file = "lxml-4.5.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c3f511a3c58676147c277eff0224c061dd5a6a8e1373572ac817ac6324f1b1e0"}, - {file = "lxml-4.5.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:59daa84aef650b11bccd18f99f64bfe44b9f14a08a28259959d33676554065a1"}, - {file = "lxml-4.5.2-cp35-cp35m-win32.whl", hash = "sha256:9dc9006dcc47e00a8a6a029eb035c8f696ad38e40a27d073a003d7d1443f5d88"}, - {file = "lxml-4.5.2-cp35-cp35m-win_amd64.whl", hash = "sha256:08fc93257dcfe9542c0a6883a25ba4971d78297f63d7a5a26ffa34861ca78730"}, - {file = "lxml-4.5.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:121b665b04083a1e85ff1f5243d4a93aa1aaba281bc12ea334d5a187278ceaf1"}, - {file = "lxml-4.5.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5591c4164755778e29e69b86e425880f852464a21c7bb53c7ea453bbe2633bbe"}, - {file = "lxml-4.5.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:cc411ad324a4486b142c41d9b2b6a722c534096963688d879ea6fa8a35028258"}, - {file = "lxml-4.5.2-cp36-cp36m-win32.whl", hash = "sha256:786aad2aa20de3dbff21aab86b2fb6a7be68064cbbc0219bde414d3a30aa47ae"}, - {file = "lxml-4.5.2-cp36-cp36m-win_amd64.whl", hash = "sha256:e1cacf4796b20865789083252186ce9dc6cc59eca0c2e79cca332bdff24ac481"}, - {file = "lxml-4.5.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:80a38b188d20c0524fe8959c8ce770a8fdf0e617c6912d23fc97c68301bb9aba"}, - {file = "lxml-4.5.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ecc930ae559ea8a43377e8b60ca6f8d61ac532fc57efb915d899de4a67928efd"}, - {file = "lxml-4.5.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a76979f728dd845655026ab991df25d26379a1a8fc1e9e68e25c7eda43004bed"}, - {file = "lxml-4.5.2-cp37-cp37m-win32.whl", hash = "sha256:5a9c8d11aa2c8f8b6043d845927a51eb9102eb558e3f936df494e96393f5fd3e"}, - {file = "lxml-4.5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:4b4a111bcf4b9c948e020fd207f915c24a6de3f1adc7682a2d92660eb4e84f1a"}, - {file = "lxml-4.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5dd20538a60c4cc9a077d3b715bb42307239fcd25ef1ca7286775f95e9e9a46d"}, - {file = "lxml-4.5.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:2b30aa2bcff8e958cd85d907d5109820b01ac511eae5b460803430a7404e34d7"}, - {file = "lxml-4.5.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:aa8eba3db3d8761db161003e2d0586608092e217151d7458206e243be5a43843"}, - {file = "lxml-4.5.2-cp38-cp38-win32.whl", hash = "sha256:107781b213cf7201ec3806555657ccda67b1fccc4261fb889ef7fc56976db81f"}, - {file = "lxml-4.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:f161af26f596131b63b236372e4ce40f3167c1b5b5d459b29d2514bd8c9dc9ee"}, - {file = "lxml-4.5.2.tar.gz", hash = "sha256:cdc13a1682b2a6241080745b1953719e7fe0850b40a5c71ca574f090a1391df6"}, -] -more-itertools = [ - {file = "more-itertools-8.4.0.tar.gz", hash = "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5"}, - {file = "more_itertools-8.4.0-py3-none-any.whl", hash = "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"}, -] -odfpy = [ - {file = "odfpy-1.4.1-py2.7.egg", hash = "sha256:fc3b8d1bc098eba4a0fda865a76d9d1e577c4ceec771426bcb169a82c5e9dfe0"}, - {file = "odfpy-1.4.1.tar.gz", hash = "sha256:db766a6e59c5103212f3cc92ec8dd50a0f3a02790233ed0b52148b70d3c438ec"}, -] -organigramme = [] -packaging = [ - {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, - {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, -] -pluggy = [ - {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, - {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, -] -psycopg2-binary = [ - {file = "psycopg2-binary-2.8.5.tar.gz", hash = "sha256:ccdc6a87f32b491129ada4b87a43b1895cf2c20fdb7f98ad979647506ffc41b6"}, - {file = "psycopg2_binary-2.8.5-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:96d3038f5bd061401996614f65d27a4ecb62d843eb4f48e212e6d129171a721f"}, - {file = "psycopg2_binary-2.8.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:08507efbe532029adee21b8d4c999170a83760d38249936038bd0602327029b5"}, - {file = "psycopg2_binary-2.8.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:b9a8b391c2b0321e0cd7ec6b4cfcc3dd6349347bd1207d48bcb752aa6c553a66"}, - {file = "psycopg2_binary-2.8.5-cp27-cp27m-win32.whl", hash = "sha256:3286541b9d85a340ee4ed42732d15fc1bb441dc500c97243a768154ab8505bb5"}, - {file = "psycopg2_binary-2.8.5-cp27-cp27m-win_amd64.whl", hash = "sha256:008da3ab51adc70a5f1cfbbe5db3a22607ab030eb44bcecf517ad11a0c2b3cac"}, - {file = "psycopg2_binary-2.8.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ba13346ff6d3eb2dca0b6fa0d8a9d999eff3dcd9b55f3a890f12b0b6362b2b38"}, - {file = "psycopg2_binary-2.8.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:c8830b7d5f16fd79d39b21e3d94f247219036b29b30c8270314c46bf8b732389"}, - {file = "psycopg2_binary-2.8.5-cp34-cp34m-win32.whl", hash = "sha256:51f7823f1b087d2020d8e8c9e6687473d3d239ba9afc162d9b2ab6e80b53f9f9"}, - {file = "psycopg2_binary-2.8.5-cp34-cp34m-win_amd64.whl", hash = "sha256:107d9be3b614e52a192719c6bf32e8813030020ea1d1215daa86ded9a24d8b04"}, - {file = "psycopg2_binary-2.8.5-cp35-cp35m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:930315ac53dc65cbf52ab6b6d27422611f5fb461d763c531db229c7e1af6c0b3"}, - {file = "psycopg2_binary-2.8.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:6bb2dd006a46a4a4ce95201f836194eb6a1e863f69ee5bab506673e0ca767057"}, - {file = "psycopg2_binary-2.8.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:3939cf75fc89c5e9ed836e228c4a63604dff95ad19aed2bbf71d5d04c15ed5ce"}, - {file = "psycopg2_binary-2.8.5-cp35-cp35m-win32.whl", hash = "sha256:a20299ee0ea2f9cca494396ac472d6e636745652a64a418b39522c120fd0a0a4"}, - {file = "psycopg2_binary-2.8.5-cp35-cp35m-win_amd64.whl", hash = "sha256:cc30cb900f42c8a246e2cb76539d9726f407330bc244ca7729c41a44e8d807fb"}, - {file = "psycopg2_binary-2.8.5-cp36-cp36m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:40abc319f7f26c042a11658bf3dd3b0b3bceccf883ec1c565d5c909a90204434"}, - {file = "psycopg2_binary-2.8.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:702f09d8f77dc4794651f650828791af82f7c2efd8c91ae79e3d9fe4bb7d4c98"}, - {file = "psycopg2_binary-2.8.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d1a8b01f6a964fec702d6b6dac1f91f2b9f9fe41b310cbb16c7ef1fac82df06d"}, - {file = "psycopg2_binary-2.8.5-cp36-cp36m-win32.whl", hash = "sha256:17a0ea0b0eabf07035e5e0d520dabc7950aeb15a17c6d36128ba99b2721b25b1"}, - {file = "psycopg2_binary-2.8.5-cp36-cp36m-win_amd64.whl", hash = "sha256:e004db88e5a75e5fdab1620fb9f90c9598c2a195a594225ac4ed2a6f1c23e162"}, - {file = "psycopg2_binary-2.8.5-cp37-cp37m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:a34826d6465c2e2bbe9d0605f944f19d2480589f89863ed5f091943be27c9de4"}, - {file = "psycopg2_binary-2.8.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cac918cd7c4c498a60f5d2a61d4f0a6091c2c9490d81bc805c963444032d0dab"}, - {file = "psycopg2_binary-2.8.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:7b832d76cc65c092abd9505cc670c4e3421fd136fb6ea5b94efbe4c146572505"}, - {file = "psycopg2_binary-2.8.5-cp37-cp37m-win32.whl", hash = "sha256:bb0608694a91db1e230b4a314e8ed00ad07ed0c518f9a69b83af2717e31291a3"}, - {file = "psycopg2_binary-2.8.5-cp37-cp37m-win_amd64.whl", hash = "sha256:eb2f43ae3037f1ef5e19339c41cf56947021ac892f668765cd65f8ab9814192e"}, - {file = "psycopg2_binary-2.8.5-cp38-cp38-macosx_10_9_x86_64.macosx_10_9_intel.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:07cf82c870ec2d2ce94d18e70c13323c89f2f2a2628cbf1feee700630be2519a"}, - {file = "psycopg2_binary-2.8.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a69970ee896e21db4c57e398646af9edc71c003bc52a3cc77fb150240fefd266"}, - {file = "psycopg2_binary-2.8.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7036ccf715925251fac969f4da9ad37e4b7e211b1e920860148a10c0de963522"}, - {file = "psycopg2_binary-2.8.5-cp38-cp38-win32.whl", hash = "sha256:8f74e631b67482d504d7e9cf364071fc5d54c28e79a093ff402d5f8f81e23bfa"}, - {file = "psycopg2_binary-2.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:fa466306fcf6b39b8a61d003123d442b23707d635a5cb05ac4e1b62cc79105cd"}, -] -py = [ - {file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"}, - {file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"}, -] -pydantic = [ - {file = "pydantic-1.6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:418b84654b60e44c0cdd5384294b0e4bc1ebf42d6e873819424f3b78b8690614"}, - {file = "pydantic-1.6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4900b8820b687c9a3ed753684337979574df20e6ebe4227381d04b3c3c628f99"}, - {file = "pydantic-1.6.1-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:b49c86aecde15cde33835d5d6360e55f5e0067bb7143a8303bf03b872935c75b"}, - {file = "pydantic-1.6.1-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:2de562a456c4ecdc80cf1a8c3e70c666625f7d02d89a6174ecf63754c734592e"}, - {file = "pydantic-1.6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f769141ab0abfadf3305d4fcf36660e5cf568a666dd3efab7c3d4782f70946b1"}, - {file = "pydantic-1.6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2dc946b07cf24bee4737ced0ae77e2ea6bc97489ba5a035b603bd1b40ad81f7e"}, - {file = "pydantic-1.6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:36dbf6f1be212ab37b5fda07667461a9219c956181aa5570a00edfb0acdfe4a1"}, - {file = "pydantic-1.6.1-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:1783c1d927f9e1366e0e0609ae324039b2479a1a282a98ed6a6836c9ed02002c"}, - {file = "pydantic-1.6.1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:cf3933c98cb5e808b62fae509f74f209730b180b1e3c3954ee3f7949e083a7df"}, - {file = "pydantic-1.6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f8af9b840a9074e08c0e6dc93101de84ba95df89b267bf7151d74c553d66833b"}, - {file = "pydantic-1.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:40d765fa2d31d5be8e29c1794657ad46f5ee583a565c83cea56630d3ae5878b9"}, - {file = "pydantic-1.6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3fa799f3cfff3e5f536cbd389368fc96a44bb30308f258c94ee76b73bd60531d"}, - {file = "pydantic-1.6.1-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:6c3f162ba175678218629f446a947e3356415b6b09122dcb364e58c442c645a7"}, - {file = "pydantic-1.6.1-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:eb75dc1809875d5738df14b6566ccf9fd9c0bcde4f36b72870f318f16b9f5c20"}, - {file = "pydantic-1.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:530d7222a2786a97bc59ee0e0ebbe23728f82974b1f1ad9a11cd966143410633"}, - {file = "pydantic-1.6.1-py36.py37.py38-none-any.whl", hash = "sha256:b5b3489cb303d0f41ad4a7390cf606a5f2c7a94dcba20c051cd1c653694cb14d"}, - {file = "pydantic-1.6.1.tar.gz", hash = "sha256:54122a8ed6b75fe1dd80797f8251ad2063ea348a03b77218d73ea9fe19bd4e73"}, -] -pyexcel = [ - {file = "pyexcel-0.6.2-py2.py3-none-any.whl", hash = "sha256:c3f918563073f0ef9a0f9e365ded074bd5baa73ef249b2783dc0d4361ed67d8b"}, - {file = "pyexcel-0.6.2.tar.gz", hash = "sha256:408c67a8a2d62a2d6979a3f8d82d6ca80aa127a4b628d046d13cdbe35666b14a"}, -] -pyexcel-ezodf = [ - {file = "pyexcel-ezodf-0.3.4.tar.gz", hash = "sha256:972eeea9b0e4bab60dfc5cdcb7378cc7ba5e070a0b7282746c0182c5de011ff1"}, - {file = "pyexcel_ezodf-0.3.4-py2.py3-none-any.whl", hash = "sha256:a74ac7636a015fff31d35c5350dc5ad347ba98ecb453de4dbcbb9a9168434e8c"}, -] -pyexcel-io = [ - {file = "pyexcel-io-0.5.20.tar.gz", hash = "sha256:08dfe39553b996359b143de3d9ec43e196f1138d47cabb73af04a16821b84d79"}, - {file = "pyexcel_io-0.5.20-py2.py3-none-any.whl", hash = "sha256:2cba956814e72b66072d97b00ede4a084ad881ce72129088eb0dc3c7f3d670cd"}, -] -pyexcel-ods = [ - {file = "pyexcel-ods-0.5.6.tar.gz", hash = "sha256:3be52fd8aaddbd8bc9286f7eb148f4553d4c97252d695c3a9ec7b95e6666a223"}, - {file = "pyexcel_ods-0.5.6-py2.py3-none-any.whl", hash = "sha256:e8d1137da99599e6053c056c21ca601d90f8e000b4db9db679302bbf55d15c0f"}, -] -pyexcel-ods3 = [ - {file = "pyexcel-ods3-0.5.3.tar.gz", hash = "sha256:3e5f8687a54a1b50d7327145cf4b777a16d9e074370bb24a1193d22cca312e76"}, - {file = "pyexcel_ods3-0.5.3-py3-none-any.whl", hash = "sha256:62aa0207ab1074a578d8959bf7d7f7c3710b4bb81423a026d8c03bf0f16b6736"}, -] -pyjwt = [ - {file = "PyJWT-1.7.1-py2.py3-none-any.whl", hash = "sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e"}, - {file = "PyJWT-1.7.1.tar.gz", hash = "sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96"}, -] -pyparsing = [ - {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, - {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, -] -pytest = [ - {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"}, - {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"}, -] -pytest-env = [ - {file = "pytest-env-0.6.2.tar.gz", hash = "sha256:7e94956aef7f2764f3c147d216ce066bf6c42948bb9e293169b1b1c880a580c2"}, -] -python-dotenv = [ - {file = "python-dotenv-0.14.0.tar.gz", hash = "sha256:8c10c99a1b25d9a68058a1ad6f90381a62ba68230ca93966882a4dbc3bc9c33d"}, - {file = "python_dotenv-0.14.0-py2.py3-none-any.whl", hash = "sha256:c10863aee750ad720f4f43436565e4c1698798d763b63234fb5021b6c616e423"}, -] -pyyaml = [ - {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, - {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, - {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, - {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, - {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, - {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, - {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, - {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, - {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, - {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, - {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, -] -requests = [ - {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"}, - {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"}, -] -sidb = [] -six = [ - {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, - {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, -] -starlette = [ - {file = "starlette-0.13.4-py3-none-any.whl", hash = "sha256:0fb4b38d22945b46acb880fedee7ee143fd6c0542992501be8c45c0ed737dd1a"}, - {file = "starlette-0.13.4.tar.gz", hash = "sha256:04fe51d86fd9a594d9b71356ed322ccde5c9b448fc716ac74155e5821a922f8d"}, -] -texttable = [ - {file = "texttable-1.6.2-py2.py3-none-any.whl", hash = "sha256:7dc282a5b22564fe0fdc1c771382d5dd9a54742047c61558e071c8cd595add86"}, - {file = "texttable-1.6.2.tar.gz", hash = "sha256:eff3703781fbc7750125f50e10f001195174f13825a92a45e9403037d539b4f4"}, -] -urllib3 = [ - {file = "urllib3-1.25.9-py2.py3-none-any.whl", hash = "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"}, - {file = "urllib3-1.25.9.tar.gz", hash = "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527"}, -] -uvicorn = [ - {file = "uvicorn-0.11.6-py3-none-any.whl", hash = "sha256:d19a20b17445708fd222e5a7cfc3eacfb31ac269bc8fefa4920833334e199782"}, - {file = "uvicorn-0.11.6.tar.gz", hash = "sha256:467c333c743ec6a3eb545517a0e3cac603becfa40c73c65ed46c3a4bf1c8e2d0"}, -] -uvloop = [ - {file = "uvloop-0.14.0-cp35-cp35m-macosx_10_11_x86_64.whl", hash = "sha256:08b109f0213af392150e2fe6f81d33261bb5ce968a288eb698aad4f46eb711bd"}, - {file = "uvloop-0.14.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:4544dcf77d74f3a84f03dd6278174575c44c67d7165d4c42c71db3fdc3860726"}, - {file = "uvloop-0.14.0-cp36-cp36m-macosx_10_11_x86_64.whl", hash = "sha256:b4f591aa4b3fa7f32fb51e2ee9fea1b495eb75b0b3c8d0ca52514ad675ae63f7"}, - {file = "uvloop-0.14.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f07909cd9fc08c52d294b1570bba92186181ca01fe3dc9ffba68955273dd7362"}, - {file = "uvloop-0.14.0-cp37-cp37m-macosx_10_11_x86_64.whl", hash = "sha256:afd5513c0ae414ec71d24f6f123614a80f3d27ca655a4fcf6cabe50994cc1891"}, - {file = "uvloop-0.14.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e7514d7a48c063226b7d06617cbb12a14278d4323a065a8d46a7962686ce2e95"}, - {file = "uvloop-0.14.0-cp38-cp38-macosx_10_11_x86_64.whl", hash = "sha256:bcac356d62edd330080aed082e78d4b580ff260a677508718f88016333e2c9c5"}, - {file = "uvloop-0.14.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4315d2ec3ca393dd5bc0b0089d23101276778c304d42faff5dc4579cb6caef09"}, - {file = "uvloop-0.14.0.tar.gz", hash = "sha256:123ac9c0c7dd71464f58f1b4ee0bbd81285d96cdda8bc3519281b8973e3a461e"}, -] -wcwidth = [ - {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, - {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, -] -websockets = [ - {file = "websockets-8.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:3762791ab8b38948f0c4d281c8b2ddfa99b7e510e46bd8dfa942a5fff621068c"}, - {file = "websockets-8.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3db87421956f1b0779a7564915875ba774295cc86e81bc671631379371af1170"}, - {file = "websockets-8.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4f9f7d28ce1d8f1295717c2c25b732c2bc0645db3215cf757551c392177d7cb8"}, - {file = "websockets-8.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:295359a2cc78736737dd88c343cd0747546b2174b5e1adc223824bcaf3e164cb"}, - {file = "websockets-8.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:1d3f1bf059d04a4e0eb4985a887d49195e15ebabc42364f4eb564b1d065793f5"}, - {file = "websockets-8.1-cp36-cp36m-win32.whl", hash = "sha256:2db62a9142e88535038a6bcfea70ef9447696ea77891aebb730a333a51ed559a"}, - {file = "websockets-8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:0e4fb4de42701340bd2353bb2eee45314651caa6ccee80dbd5f5d5978888fed5"}, - {file = "websockets-8.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:9b248ba3dd8a03b1a10b19efe7d4f7fa41d158fdaa95e2cf65af5a7b95a4f989"}, - {file = "websockets-8.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ce85b06a10fc65e6143518b96d3dca27b081a740bae261c2fb20375801a9d56d"}, - {file = "websockets-8.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:965889d9f0e2a75edd81a07592d0ced54daa5b0785f57dc429c378edbcffe779"}, - {file = "websockets-8.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:751a556205d8245ff94aeef23546a1113b1dd4f6e4d102ded66c39b99c2ce6c8"}, - {file = "websockets-8.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3ef56fcc7b1ff90de46ccd5a687bbd13a3180132268c4254fc0fa44ecf4fc422"}, - {file = "websockets-8.1-cp37-cp37m-win32.whl", hash = "sha256:7ff46d441db78241f4c6c27b3868c9ae71473fe03341340d2dfdbe8d79310acc"}, - {file = "websockets-8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:20891f0dddade307ffddf593c733a3fdb6b83e6f9eef85908113e628fa5a8308"}, - {file = "websockets-8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c1ec8db4fac31850286b7cd3b9c0e1b944204668b8eb721674916d4e28744092"}, - {file = "websockets-8.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5c01fd846263a75bc8a2b9542606927cfad57e7282965d96b93c387622487485"}, - {file = "websockets-8.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9bef37ee224e104a413f0780e29adb3e514a5b698aabe0d969a6ba426b8435d1"}, - {file = "websockets-8.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d705f8aeecdf3262379644e4b55107a3b55860eb812b673b28d0fbc347a60c55"}, - {file = "websockets-8.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:c8a116feafdb1f84607cb3b14aa1418424ae71fee131642fc568d21423b51824"}, - {file = "websockets-8.1-cp38-cp38-win32.whl", hash = "sha256:e898a0863421650f0bebac8ba40840fc02258ef4714cb7e1fd76b6a6354bda36"}, - {file = "websockets-8.1-cp38-cp38-win_amd64.whl", hash = "sha256:f8a7bff6e8664afc4e6c28b983845c5bc14965030e3fb98789734d416af77c4b"}, - {file = "websockets-8.1.tar.gz", hash = "sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f"}, -] -zipp = [ - {file = "zipp-3.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"}, - {file = "zipp-3.1.0.tar.gz", hash = "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"}, -] diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 096399f..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,35 +0,0 @@ -[tool.poetry] -name = "halfapi" -version = "0.1.0" -description = "The core module for an halfORM API" -authors = ["Joël Maizi ", "Maxime Alves "] -homepage = "https://gite.lirmm.fr/newsi/api/halfapi" - -[tool.poetry.dependencies] -click = "^7" -half-orm = { git = "git@gite.lirmm.fr:newsi/halfORM.git" } -PyJWT = "^1" -python = "^3.7" -starlette = "^0" -uvicorn = { version = "^0" } -apidb = { git = "git@gite.lirmm.fr:newsi/db/hop_api.git" } -fastapi = { version = "^0", optional = true } -organigramme = { git = "git@gite.lirmm.fr:newsi/api/organigramme.git", optional = true } -sidb = { git = "git@gite.lirmm.fr:newsi/sidb_halfORM.git", optional = true } -python-dotenv = "^0.14.0" -pyexcel-ods3 = { version = "^0.5.3", optional = true } - -[tool.poetry.dev-dependencies] -pytest = "^5" -requests = "^2" -pytest-env = "^0.6.2" - -[tool.poetry.extras] -organigramme = [ "fastapi", "organigramme", "sidb", "pyexcel-ods3" ] - -[tool.poetry.scripts] -halfapi = 'halfapi.cli:cli' -[build-system] -requires = ["poetry>=0.12"] -build-backend = "poetry.masonry.api" - diff --git a/requirements.txt b/requirements.txt index 8579c24..9296ac6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,3 @@ starlette uvicorn jwt half_orm @ git+ssh://git@gite.lirmm.fr/newsi/halfORM.git -organigramme @ git+ssh://git@gite.lirmm.fr/api/newsi/halfapi.git diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..0a4fbea --- /dev/null +++ b/setup.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +import re + +from setuptools import setup, find_packages + + +def get_version(package): + """ + Return package version as listed in `__version__` in `init.py`. + """ + with open(os.path.join(package, "__init__.py")) as f: + return re.search("__version__ = ['\"]([^'\"]+)['\"]", f.read()).group(1) + + +def get_long_description(): + """ + Return the README. + """ + with open("README.md", encoding="utf8") as f: + return f.read() + + +def get_packages(package): + """ + Return root package and all sub-packages. + """ + return [ + dirpath + for dirpath, dirnames, filenames in os.walk(package) + if os.path.exists(os.path.join(dirpath, "__init__.py")) + ] + +module_name="halfapi" +setup( + name=module_name, + python_requires=">=3.7", + version=get_version(module_name), + url="https://gite.lirmm.fr/newsi/api/halfapi", + long_description=get_long_description(), + long_description_content_type="text/markdown", + packages=get_packages(module_name), + package_data={ + 'halfapi': ['lib/*', 'models/*'] + }, + install_requires=[ + "click", + "half_orm", + "jwt", + "starlette", + "uvicorn"], + extras_require={ + "tests":["pytest", "requests"], + }, + entry_points={ + "console_scripts":[ + "halfapi=halfapi.cli:cli" + ] + } +)