Compare commits
	
		
			2 Commits
		
	
	
		
			9bcba9c7f9
			...
			a6cbc002f6
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
								 | 
						a6cbc002f6 | |
| 
							
							
								
								 | 
						34ee95c66a | 
| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
import io
 | 
			
		||||
import sys
 | 
			
		||||
import inspect
 | 
			
		||||
import logging
 | 
			
		||||
| 
						 | 
				
			
			@ -93,15 +94,32 @@ class InterfaceGenerator:
 | 
			
		|||
        self.basename = models.__name__
 | 
			
		||||
        self.walker(("models", models))
 | 
			
		||||
 | 
			
		||||
    def _write_types(self):
 | 
			
		||||
        TYPE = "type {name} = {alias};\n"
 | 
			
		||||
        INTERFACE = "interface {name} {{\n{properties}}}\n"
 | 
			
		||||
        PROPERTY = "\t{name}{modifier}: {type};\n"
 | 
			
		||||
 | 
			
		||||
        buffer = io.StringIO()
 | 
			
		||||
        for cls, props in self.classes.items():
 | 
			
		||||
            if isinstance(props, str):
 | 
			
		||||
                buffer.write(TYPE.format(name=cls, alias=props))
 | 
			
		||||
            else:
 | 
			
		||||
                buffer.write(
 | 
			
		||||
                    INTERFACE.format(
 | 
			
		||||
                        name=cls,
 | 
			
		||||
                        properties="".join(
 | 
			
		||||
                            [PROPERTY.format(name=name, modifier=props[name][0], type=props[name][1]) for name in props]
 | 
			
		||||
                        ),
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
        return buffer
 | 
			
		||||
 | 
			
		||||
    def write(self):
 | 
			
		||||
        with (open(self.filename, "w") if self.filename else sys.stdout) as file:
 | 
			
		||||
            file.write("declare namespace {} {{\n".format(self.namespace))
 | 
			
		||||
            for cls, params in self.classes.items():
 | 
			
		||||
                if isinstance(params, str):
 | 
			
		||||
                    file.write("\ttype {} = {};\n".format(cls, params))
 | 
			
		||||
                else:
 | 
			
		||||
                    file.write("\tinterface {} {{\n".format(cls))
 | 
			
		||||
                    for name in params:
 | 
			
		||||
                        file.write("\t\t{}{}: {};\n".format(name, *params[name]))
 | 
			
		||||
                    file.write("\t}\n")
 | 
			
		||||
            if self.namespace:
 | 
			
		||||
                file.write(f"declare namespace {self.namespace} {{\n")
 | 
			
		||||
                for line in self._write_types().getvalue().split("\n"):
 | 
			
		||||
                    file.write(f"\t{line}\n")
 | 
			
		||||
                file.write("}\n")
 | 
			
		||||
            else:
 | 
			
		||||
                file.write(self._write_types().getvalue())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,5 @@
 | 
			
		|||
import pathlib
 | 
			
		||||
import subprocess
 | 
			
		||||
import click
 | 
			
		||||
from os import environ
 | 
			
		||||
from flask import current_app
 | 
			
		||||
from flask.cli import FlaskGroup, run_command, with_appcontext
 | 
			
		||||
import pkg_resources
 | 
			
		||||
from flask.cli import FlaskGroup, with_appcontext
 | 
			
		||||
from flaschengeist.app import create_app
 | 
			
		||||
from flaschengeist.config import configure_logger
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -63,98 +58,14 @@ def cli():
 | 
			
		|||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@cli.command()
 | 
			
		||||
@with_appcontext
 | 
			
		||||
def install():
 | 
			
		||||
    """Install and initialize enabled plugins.
 | 
			
		||||
def main(*args, **kwargs):
 | 
			
		||||
    from .plugin_cmd import plugin
 | 
			
		||||
    from .export_cmd import export
 | 
			
		||||
    from .docs_cmd import docs
 | 
			
		||||
    from .run_cmd import run
 | 
			
		||||
 | 
			
		||||
    Most plugins need to install custom tables into the database
 | 
			
		||||
    running this command will lookup all enabled plugins and run
 | 
			
		||||
    their database initalization routines.
 | 
			
		||||
    """
 | 
			
		||||
    from flaschengeist.app import install_all
 | 
			
		||||
 | 
			
		||||
    install_all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@cli.command()
 | 
			
		||||
@click.option("--output", "-o", help="Output file, default is stdout", type=click.Path())
 | 
			
		||||
@click.option("--namespace", "-n", help="TS namespace for the interfaces", type=str, show_default=True)
 | 
			
		||||
@click.option("--plugin", "-p", help="Also export types for a plugin (even if disabled)", multiple=True, type=str)
 | 
			
		||||
@click.option("--no-core", help="Skip models / types from flaschengeist core", is_flag=True)
 | 
			
		||||
def export(namespace, output, no_core, plugin):
 | 
			
		||||
    from flaschengeist import models
 | 
			
		||||
    from flaschengeist import logger
 | 
			
		||||
    from .InterfaceGenerator import InterfaceGenerator
 | 
			
		||||
 | 
			
		||||
    gen = InterfaceGenerator(namespace, output, logger)
 | 
			
		||||
    if not no_core:
 | 
			
		||||
        gen.run(models)
 | 
			
		||||
    if plugin:
 | 
			
		||||
        for entry_point in pkg_resources.iter_entry_points("flaschengeist.plugins"):
 | 
			
		||||
            if len(plugin) == 0 or entry_point.name in plugin:
 | 
			
		||||
                plg = entry_point.load()
 | 
			
		||||
                if hasattr(plg, "models") and plg.models is not None:
 | 
			
		||||
                    gen.run(plg.models)
 | 
			
		||||
    gen.write()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@cli.command()
 | 
			
		||||
@click.option("--host", help="set hostname to listen on", default="127.0.0.1", show_default=True)
 | 
			
		||||
@click.option("--port", help="set port to listen on", type=int, default=5000, show_default=True)
 | 
			
		||||
@click.option("--debug", help="run in debug mode", is_flag=True)
 | 
			
		||||
@with_appcontext
 | 
			
		||||
@click.pass_context
 | 
			
		||||
def run(ctx, host, port, debug):
 | 
			
		||||
    """Run Flaschengeist using a development server."""
 | 
			
		||||
 | 
			
		||||
    class PrefixMiddleware(object):
 | 
			
		||||
        def __init__(self, app, prefix=""):
 | 
			
		||||
            self.app = app
 | 
			
		||||
            self.prefix = prefix
 | 
			
		||||
 | 
			
		||||
        def __call__(self, environ, start_response):
 | 
			
		||||
 | 
			
		||||
            if environ["PATH_INFO"].startswith(self.prefix):
 | 
			
		||||
                environ["PATH_INFO"] = environ["PATH_INFO"][len(self.prefix) :]
 | 
			
		||||
                environ["SCRIPT_NAME"] = self.prefix
 | 
			
		||||
                return self.app(environ, start_response)
 | 
			
		||||
            else:
 | 
			
		||||
                start_response("404", [("Content-Type", "text/plain")])
 | 
			
		||||
                return ["This url does not belong to the app.".encode()]
 | 
			
		||||
 | 
			
		||||
    from flaschengeist.config import config
 | 
			
		||||
 | 
			
		||||
    # re configure logger, as we are no logger in CLI mode
 | 
			
		||||
    configure_logger()
 | 
			
		||||
    current_app.wsgi_app = PrefixMiddleware(current_app.wsgi_app, prefix=config["FLASCHENGEIST"].get("root", ""))
 | 
			
		||||
    if debug:
 | 
			
		||||
        environ["FLASK_DEBUG"] = "1"
 | 
			
		||||
        environ["FLASK_ENV"] = "development"
 | 
			
		||||
 | 
			
		||||
    ctx.invoke(run_command, host=host, port=port, debugger=debug)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@cli.command()
 | 
			
		||||
@click.option(
 | 
			
		||||
    "--output",
 | 
			
		||||
    "-o",
 | 
			
		||||
    help="Documentation output path",
 | 
			
		||||
    default="./docs",
 | 
			
		||||
    type=click.Path(file_okay=False, path_type=pathlib.Path),
 | 
			
		||||
)
 | 
			
		||||
def docs(output: pathlib.Path):
 | 
			
		||||
    """Generate and export API documentation using pdoc3"""
 | 
			
		||||
    output.mkdir(parents=True, exist_ok=True)
 | 
			
		||||
    command = [
 | 
			
		||||
        "python",
 | 
			
		||||
        "-m",
 | 
			
		||||
        "pdoc",
 | 
			
		||||
        "--skip-errors",
 | 
			
		||||
        "--html",
 | 
			
		||||
        "--output-dir",
 | 
			
		||||
        str(output),
 | 
			
		||||
        "flaschengeist",
 | 
			
		||||
    ]
 | 
			
		||||
    click.echo(f"Running command: {command}")
 | 
			
		||||
    subprocess.check_call(command)
 | 
			
		||||
    cli.add_command(plugin)
 | 
			
		||||
    cli.add_command(export)
 | 
			
		||||
    cli.add_command(docs)
 | 
			
		||||
    cli.add_command(run)
 | 
			
		||||
    cli(*args, **kwargs)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
import click
 | 
			
		||||
import pathlib
 | 
			
		||||
import subprocess
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@click.command()
 | 
			
		||||
@click.option(
 | 
			
		||||
    "--output",
 | 
			
		||||
    "-o",
 | 
			
		||||
    help="Documentation output path",
 | 
			
		||||
    default="./docs",
 | 
			
		||||
    type=click.Path(file_okay=False, path_type=pathlib.Path),
 | 
			
		||||
)
 | 
			
		||||
@click.pass_context
 | 
			
		||||
def docs(ctx: click.Context, output: pathlib.Path):
 | 
			
		||||
    """Generate and export API documentation using pdoc"""
 | 
			
		||||
    import pkg_resources
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        pkg_resources.get_distribution("pdoc>=8.0.1")
 | 
			
		||||
    except pkg_resources.DistributionNotFound:
 | 
			
		||||
        click.echo(
 | 
			
		||||
            f"Error: pdoc was not found, maybe you need to install it. Try:\n" "\n" '$ pip install "pdoc>=8.0.1"\n'
 | 
			
		||||
        )
 | 
			
		||||
        ctx.exit(1)
 | 
			
		||||
    output.mkdir(parents=True, exist_ok=True)
 | 
			
		||||
    command = [
 | 
			
		||||
        "python",
 | 
			
		||||
        "-m",
 | 
			
		||||
        "pdoc",
 | 
			
		||||
        "--docformat",
 | 
			
		||||
        "google",
 | 
			
		||||
        "--output-directory",
 | 
			
		||||
        str(output),
 | 
			
		||||
        "flaschengeist",
 | 
			
		||||
    ]
 | 
			
		||||
    click.echo(f"Running command: {command}")
 | 
			
		||||
    subprocess.check_call(command)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
import click
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@click.command()
 | 
			
		||||
@click.option("--output", "-o", help="Output file, default is stdout", type=click.Path())
 | 
			
		||||
@click.option("--namespace", "-n", help="TS namespace for the interfaces", type=str, show_default=True)
 | 
			
		||||
@click.option("--plugin", "-p", help="Also export types for a plugin (even if disabled)", multiple=True, type=str)
 | 
			
		||||
@click.option("--no-core", help="Skip models / types from flaschengeist core", is_flag=True)
 | 
			
		||||
def export(namespace, output, no_core, plugin):
 | 
			
		||||
    from flaschengeist import logger, models
 | 
			
		||||
    from flaschengeist.app import get_plugins
 | 
			
		||||
    from .InterfaceGenerator import InterfaceGenerator
 | 
			
		||||
 | 
			
		||||
    gen = InterfaceGenerator(namespace, output, logger)
 | 
			
		||||
    if not no_core:
 | 
			
		||||
        gen.run(models)
 | 
			
		||||
    if plugin:
 | 
			
		||||
        for plugin_class in get_plugins():
 | 
			
		||||
            if (len(plugin) == 0 or plugin_class.id in plugin) and plugin_class.models is not None:
 | 
			
		||||
                gen.run(plugin_class.models)
 | 
			
		||||
    gen.write()
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
import click
 | 
			
		||||
from os import environ
 | 
			
		||||
from flask import current_app
 | 
			
		||||
from flask.cli import with_appcontext, run_command
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PrefixMiddleware(object):
 | 
			
		||||
    def __init__(self, app, prefix=""):
 | 
			
		||||
        self.app = app
 | 
			
		||||
        self.prefix = prefix
 | 
			
		||||
 | 
			
		||||
    def __call__(self, environ, start_response):
 | 
			
		||||
 | 
			
		||||
        if environ["PATH_INFO"].startswith(self.prefix):
 | 
			
		||||
            environ["PATH_INFO"] = environ["PATH_INFO"][len(self.prefix) :]
 | 
			
		||||
            environ["SCRIPT_NAME"] = self.prefix
 | 
			
		||||
            return self.app(environ, start_response)
 | 
			
		||||
        else:
 | 
			
		||||
            start_response("404", [("Content-Type", "text/plain")])
 | 
			
		||||
            return ["This url does not belong to the app.".encode()]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@click.command()
 | 
			
		||||
@click.option("--host", help="set hostname to listen on", default="127.0.0.1", show_default=True)
 | 
			
		||||
@click.option("--port", help="set port to listen on", type=int, default=5000, show_default=True)
 | 
			
		||||
@click.option("--debug", help="run in debug mode", is_flag=True)
 | 
			
		||||
@with_appcontext
 | 
			
		||||
@click.pass_context
 | 
			
		||||
def run(ctx, host, port, debug):
 | 
			
		||||
    """Run Flaschengeist using a development server."""
 | 
			
		||||
    from flaschengeist.config import config, configure_logger
 | 
			
		||||
 | 
			
		||||
    # re configure logger, as we are no logger in CLI mode
 | 
			
		||||
    configure_logger()
 | 
			
		||||
    current_app.wsgi_app = PrefixMiddleware(current_app.wsgi_app, prefix=config["FLASCHENGEIST"].get("root", ""))
 | 
			
		||||
    if debug:
 | 
			
		||||
        environ["FLASK_DEBUG"] = "1"
 | 
			
		||||
        environ["FLASK_ENV"] = "development"
 | 
			
		||||
 | 
			
		||||
    ctx.invoke(run_command, host=host, port=port, debugger=debug)
 | 
			
		||||
		Loading…
	
		Reference in New Issue