예제 #1
0
파일: reference.py 프로젝트: zkan/piccolo
    def resolve(self) -> t.Type[Table]:
        if self.app_name is not None:
            from piccolo.conf.apps import Finder

            finder = Finder()
            return finder.get_table_with_name(
                app_name=self.app_name, table_class_name=self.table_class_name)

        if self.module_path:
            module = importlib.import_module(self.module_path)
            table: t.Optional[t.Type[Table]] = getattr(module,
                                                       self.table_class_name,
                                                       None)

            from piccolo.table import Table

            if (table is not None and inspect.isclass(table)
                    and issubclass(table, Table)):
                return table
            else:
                raise ValueError(
                    f"Can't find a Table subclass called {self.app_name} "
                    f"in {self.module_path}")

        raise ValueError("You must specify either app_name or module_path.")
예제 #2
0
def create_pydantic_fixture_model(fixture_configs: t.List[FixtureConfig]):
    """
    Returns a nested Pydantic model for serialising and deserialising fixtures.
    """
    columns: t.Dict[str, t.Any] = {}

    finder = Finder()

    for fixture_config in fixture_configs:

        app_columns: t.Dict[str, t.Any] = {}

        for table_class_name in fixture_config.table_class_names:
            table_class: t.Type[Table] = finder.get_table_with_name(
                app_name=fixture_config.app_name,
                table_class_name=table_class_name,
            )
            app_columns[table_class_name] = (
                t.List[  # type: ignore
                    create_pydantic_model(table_class,
                                          include_default_columns=True)],
                ...,
            )

        app_model: t.Any = pydantic.create_model(
            f"{fixture_config.app_name.title()}Model", **app_columns)

        columns[fixture_config.app_name] = (app_model, ...)

    model: t.Type[pydantic.BaseModel] = pydantic.create_model(
        "FixtureModel", **columns)

    return model
예제 #3
0
def main():
    """
    The entrypoint to the Piccolo CLI.
    """
    # In case it's run from an entrypoint:
    sys.path.insert(0, os.getcwd())

    ###########################################################################
    # Run in diagnose mode if requested.

    diagnose = get_diagnose_flag()
    if diagnose:
        print("Diagnosis...")
        if Finder(diagnose=True).get_app_registry():
            print("Everything OK")
        return

    ###########################################################################

    cli = CLI(description="Piccolo CLI")

    ###########################################################################
    # Register the base apps.

    for _app_config in [
            app_config,
            asgi_config,
            meta_config,
            migrations_config,
            playground_config,
            project_config,
            shell_config,
            sql_shell_config,
            user_config,
    ]:
        for command in _app_config.commands:
            cli.register(command, group_name=_app_config.app_name)

    ###########################################################################
    # Get user defined apps.

    try:
        APP_REGISTRY: AppRegistry = Finder().get_app_registry()
    except (ImportError, AttributeError):
        print("Can't import the APP_REGISTRY from piccolo_conf - some "
              "commands may be missing. If this is a new project don't worry. "
              f"To see a full traceback use `piccolo {DIAGNOSE_FLAG}`")
    else:
        for app_name, _app_config in APP_REGISTRY.app_configs.items():
            for command in _app_config.commands:
                if cli.command_exists(group_name=app_name,
                                      command_name=command.__name__):
                    # Skipping - already registered.
                    continue
                cli.register(command, group_name=app_name)

    ###########################################################################

    cli.run()
예제 #4
0
def run():
    """
    Runs an iPython shell, and automatically imports all of the Table classes
    from your project.
    """
    app_registry: AppRegistry = Finder().get_app_registry()

    tables = {}
    spacer = "-------"

    if app_registry.app_configs:
        print(spacer)

        for app_name, app_config in app_registry.app_configs.items():
            app_config: AppConfig = app_config
            print(f"Importing {app_name} tables:")
            if app_config.table_classes:
                for table_class in sorted(
                    app_config.table_classes, key=lambda x: x.__name__
                ):
                    table_class_name = table_class.__name__
                    print(f"- {table_class_name}")
                    tables[table_class_name] = table_class
            else:
                print("- None")

        print(spacer)

    start_ipython_shell(**tables)
예제 #5
0
파일: load.py 프로젝트: piccolo-orm/piccolo
async def load_json_string(json_string: str):
    """
    Parses the JSON string, and inserts the parsed data into the database.
    """
    # We have to deserialise the JSON to find out which apps and tables it
    # contains, so we can create a Pydantic model.
    # Then we let Pydantic do the proper deserialisation, as it does a much
    # better job of deserialising dates, datetimes, bytes etc.
    deserialised_contents = load_json(json_string)

    app_names = deserialised_contents.keys()

    fixture_configs = [
        FixtureConfig(
            app_name=app_name,
            table_class_names=[
                i for i in deserialised_contents[app_name].keys()
            ],
        ) for app_name in app_names
    ]
    pydantic_model_class = create_pydantic_fixture_model(
        fixture_configs=fixture_configs)

    fixture_pydantic_model = pydantic_model_class.parse_raw(json_string)

    finder = Finder()
    engine = engine_finder()

    if not engine:
        raise Exception("Unable to find the engine.")

    async with engine.transaction():
        for app_name in app_names:
            app_model = getattr(fixture_pydantic_model, app_name)

            for (
                    table_class_name,
                    model_instance_list,
            ) in app_model.__dict__.items():
                table_class = finder.get_table_with_name(
                    app_name, table_class_name)

                await table_class.insert(*[
                    table_class.from_dict(row.__dict__)
                    for row in model_instance_list
                ]).run()
예제 #6
0
파일: show_all.py 프로젝트: zkan/piccolo
def show_all():
    """
    Lists all registered Piccolo apps.
    """
    app_registry = Finder().get_app_registry()

    print("Registered apps:")

    for app_path in app_registry.apps:
        print(app_path)
예제 #7
0
async def new(app_name: str, auto: bool = False):
    """
    Creates a new migration file in the migrations folder.

    :param app_name:
        The app to create a migration for.
    :param auto:
        Auto create the migration contents.

    """
    print("Creating new migration ...")

    engine = Finder().get_engine()
    if auto and isinstance(engine, SQLiteEngine):
        sys.exit("Auto migrations aren't currently supported by SQLite.")

    app_config = Finder().get_app_config(app_name=app_name)

    _create_migrations_folder(app_config.migrations_folder_path)
    await _create_new_migration(app_config=app_config, auto=auto)
예제 #8
0
def engine_finder(module_name: t.Optional[str] = None) -> t.Optional[Engine]:
    """
    An example module name is `my_piccolo_conf`.

    The value used is determined by:
    module_name argument > environment variable > default.

    The module must be available on the path, so Python can import it.
    """
    from piccolo.conf.apps import Finder

    return Finder().get_engine(module_name=module_name)
예제 #9
0
파일: new.py 프로젝트: piccolo-orm/piccolo
async def new(
    app_name: str,
    auto: bool = False,
    desc: str = "",
    auto_input: t.Optional[str] = None,
):
    """
    Creates a new migration file in the migrations folder.

    :param app_name:
        The app to create a migration for.
    :param auto:
        Auto create the migration contents.
    :param desc:
        A description of what the migration does, for example --desc='adding
        name column'.
    :param auto_input:
        If provided, all prompts for user input will automatically have this
        entered. For example, --auto_input='y'.

    """
    print("Creating new migration ...")

    engine = Finder().get_engine()
    if auto and isinstance(engine, SQLiteEngine):
        sys.exit("Auto migrations aren't currently supported by SQLite.")

    app_config = Finder().get_app_config(app_name=app_name)

    _create_migrations_folder(app_config.migrations_folder_path)
    try:
        await _create_new_migration(
            app_config=app_config,
            auto=auto,
            description=desc,
            auto_input=auto_input,
        )
    except NoChanges:
        print("No changes detected - exiting.")
        sys.exit(0)
예제 #10
0
def parse_args(apps: str, tables: str) -> t.List[FixtureConfig]:
    """
    Works out which apps and tables the user is referring to.
    """
    finder = Finder()
    app_names = []

    if apps == "all":
        app_names = finder.get_sorted_app_names()
    elif "," in apps:
        app_names = apps.split(",")
    else:
        # Must be a single app name
        app_names.append(apps)

    table_class_names: t.Optional[t.List[str]] = None

    if tables != "all":
        table_class_names = tables.split(",") if "," in tables else [tables]
    output: t.List[FixtureConfig] = []

    for app_name in app_names:
        app_config = finder.get_app_config(app_name=app_name)
        table_classes = app_config.table_classes

        if table_class_names is None:
            fixture_configs = [i.__name__ for i in table_classes]
        else:
            fixture_configs = [
                i.__name__ for i in table_classes
                if i.__name__ in table_class_names
            ]
        output.append(
            FixtureConfig(
                app_name=app_name,
                table_class_names=fixture_configs,
            ))

    return output
예제 #11
0
async def get_dump(
    fixture_configs: t.List[FixtureConfig], ) -> t.Dict[str, t.Any]:
    """
    Gets the data for each table specified and returns a data structure like:

    .. code-block:: python

        {
            'my_app_name': {
                'MyTableName': [
                    {
                        'id': 1,
                        'my_column_name': 'foo'
                    }
                ]
            }
        }

    """
    finder = Finder()

    output: t.Dict[str, t.Any] = {}

    for fixture_config in fixture_configs:
        app_config = finder.get_app_config(app_name=fixture_config.app_name)
        table_classes = [
            i for i in app_config.table_classes
            if i.__name__ in fixture_config.table_class_names
        ]
        sorted_table_classes = sort_table_classes(table_classes)

        output[fixture_config.app_name] = {}

        for table_class in sorted_table_classes:
            data = await table_class.select().run()
            output[fixture_config.app_name][table_class.__name__] = data

    return output
예제 #12
0
    def test_get_table_classes(self):
        """
        Make sure ``Table`` classes can be retrieved.
        """
        finder = Finder()

        self.assertEqual(
            finder.get_table_classes(),
            [
                Manager,
                Band,
                Venue,
                Concert,
                Ticket,
                Poster,
                Shirt,
                RecordingStudio,
                MegaTable,
                SmallTable,
            ],
        )

        self.assertEqual(
            finder.get_table_classes(include_apps=["music"]),
            [
                Manager,
                Band,
                Venue,
                Concert,
                Ticket,
                Poster,
                Shirt,
                RecordingStudio,
            ],
        )

        self.assertEqual(
            finder.get_table_classes(exclude_apps=["music"]),
            [
                MegaTable,
                SmallTable,
            ],
        )

        with self.assertRaises(ValueError):
            # You shouldn't be allowed to specify both include and exclude.
            finder.get_table_classes(exclude_apps=["music"],
                                     include_apps=["mega"])
예제 #13
0
파일: main.py 프로젝트: piccolo-orm/piccolo
def main():
    """
    The entrypoint to the Piccolo CLI.
    """
    # In case it's run from an entrypoint:
    sys.path.insert(0, os.getcwd())

    ###########################################################################
    # Run in diagnose mode if requested.

    diagnose = get_diagnose_flag()
    if diagnose:
        print("Diagnosis...")
        if Finder(diagnose=True).get_app_registry():
            print("Everything OK")
        return

    ###########################################################################

    cli = CLI(description="Piccolo CLI")

    ###########################################################################
    # Register the base apps.

    for _app_config in [
            app_config,
            asgi_config,
            fixtures_config,
            meta_config,
            migrations_config,
            playground_config,
            project_config,
            schema_config,
            shell_config,
            sql_shell_config,
            tester_config,
            user_config,
    ]:
        for command in _app_config.commands:
            cli.register(
                command.callable,
                group_name=_app_config.app_name,
                aliases=command.aliases,
            )

    ###########################################################################
    # Get user defined apps.

    try:
        APP_REGISTRY: AppRegistry = Finder().get_app_registry()
    except (ImportError, AttributeError):
        print("Can't import the APP_REGISTRY from piccolo_conf - some "
              "commands may be missing. If this is a new project don't worry. "
              f"To see a full traceback use `piccolo {DIAGNOSE_FLAG}`")
    else:
        for app_name, _app_config in APP_REGISTRY.app_configs.items():
            for command in _app_config.commands:
                if cli.command_exists(group_name=app_name,
                                      command_name=command.callable.__name__):
                    # Skipping - already registered.
                    continue
                cli.register(
                    command.callable,
                    group_name=app_name,
                    aliases=command.aliases,
                )

        if "migrations" not in sys.argv:
            # Show a warning if any migrations haven't been run.
            # Don't run it if it looks like the user is running a migration
            # command, as this information is redundant.

            try:
                havent_ran_count = run_sync(
                    CheckMigrationManager(app_name="all").havent_ran_count())
                if havent_ran_count:
                    message = (f"{havent_ran_count} migration hasn't"
                               if havent_ran_count == 1 else
                               f"{havent_ran_count} migrations haven't")

                    colored_warning(
                        message=("=> {} been run - the app "
                                 "might not behave as expected.\n"
                                 "To check which use:\n"
                                 "    piccolo migrations check\n"
                                 "To run all migrations:\n"
                                 "    piccolo migrations forwards all\n"
                                 ).format(message),
                        level=Level.high,
                    )
            except Exception:
                pass

    ###########################################################################

    cli.run()
예제 #14
0
def graph(
    apps: str = "all", direction: str = "LR", output: t.Optional[str] = None
):
    """
    Prints out a graphviz .dot file for your schema.

    :param apps:
        The name of the apps to include. If 'all' is given then every app is
        included. To specify multiple app names, separate them with commas.
        For example --apps="app1,app2".
    :param direction:
        How the tables should be orientated - by default it's "LR" which is
        left to right, so the graph will be landscape. The alternative is
        "TB", which is top to bottom, so the graph will be portrait.
    :param output:
        If specified, rather than printing out the file contents, they'll be
        written to this file. For example --output=graph.dot

    """
    finder = Finder()
    app_names = finder.get_sorted_app_names()

    if apps != "all":
        given_app_names = [i.strip() for i in apps.split(",")]
        delta = set(given_app_names) - set(app_names)
        if delta:
            sys.exit(f"These apps aren't recognised: {', '.join(delta)}.")
        app_names = given_app_names

    tables: t.List[GraphTable] = []
    relations: t.List[GraphRelation] = []

    for app_name in app_names:
        app_config = finder.get_app_config(app_name=app_name)
        for table_class in app_config.table_classes:
            tables.append(
                GraphTable(
                    name=table_class.__name__,
                    columns=[
                        GraphColumn(
                            name=i._meta.name, type=i.__class__.__name__
                        )
                        for i in table_class._meta.columns
                    ],
                )
            )
            for fk_column in table_class._meta.foreign_key_columns:
                reference_table_class = (
                    fk_column._foreign_key_meta.resolved_references
                )
                relations.append(
                    GraphRelation(
                        table_a=table_class.__name__,
                        table_b=reference_table_class.__name__,
                        label=fk_column._meta.name,
                    )
                )

    contents = render_template(
        tables=tables, relations=relations, direction=direction
    )

    if output is None:
        print(contents)
    else:
        with open(output, "w") as f:
            f.write(contents)