Best Python code snippet using lisa_python
cli.py
Source:cli.py  
...144    views=False,145):146    """List the tables in the database"""147    db = sqlite_utils.Database(path)148    _load_extensions(db, load_extension)149    headers = ["view" if views else "table"]150    if counts:151        headers.append("count")152    if columns:153        headers.append("columns")154    if schema:155        headers.append("schema")156    def _iter():157        if views:158            items = db.view_names()159        else:160            items = db.table_names(fts4=fts4, fts5=fts5)161        for name in items:162            row = [name]163            if counts:164                row.append(db[name].count)165            if columns:166                cols = [c.name for c in db[name].columns]167                if csv:168                    row.append("\n".join(cols))169                else:170                    row.append(cols)171            if schema:172                row.append(db[name].schema)173            yield row174    if table:175        print(tabulate.tabulate(_iter(), headers=headers, tablefmt=fmt))176    elif csv or tsv:177        writer = csv_std.writer(sys.stdout, dialect="excel-tab" if tsv else "excel")178        if not no_headers:179            writer.writerow(headers)180        for row in _iter():181            writer.writerow(row)182    else:183        for line in output_rows(_iter(), headers, nl, arrays, json_cols):184            click.echo(line)185@cli.command()186@click.argument(187    "path",188    type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),189    required=True,190)191@click.option(192    "--counts", help="Include row counts per view", default=False, is_flag=True193)194@output_options195@click.option(196    "--columns",197    help="Include list of columns for each view",198    is_flag=True,199    default=False,200)201@click.option(202    "--schema",203    help="Include schema for each view",204    is_flag=True,205    default=False,206)207@load_extension_option208def views(209    path,210    counts,211    nl,212    arrays,213    csv,214    tsv,215    no_headers,216    table,217    fmt,218    json_cols,219    columns,220    schema,221    load_extension,222):223    """List the views in the database"""224    tables.callback(225        path=path,226        fts4=False,227        fts5=False,228        counts=counts,229        nl=nl,230        arrays=arrays,231        csv=csv,232        tsv=tsv,233        no_headers=no_headers,234        table=table,235        fmt=fmt,236        json_cols=json_cols,237        columns=columns,238        schema=schema,239        load_extension=load_extension,240        views=True,241    )242@cli.command()243@click.argument(244    "path",245    type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),246    required=True,247)248@click.argument("tables", nargs=-1)249@click.option("--no-vacuum", help="Don't run VACUUM", default=False, is_flag=True)250@load_extension_option251def optimize(path, tables, no_vacuum, load_extension):252    """Optimize all full-text search tables and then run VACUUM - should shrink the database file"""253    db = sqlite_utils.Database(path)254    _load_extensions(db, load_extension)255    if not tables:256        tables = db.table_names(fts4=True) + db.table_names(fts5=True)257    with db.conn:258        for table in tables:259            db[table].optimize()260    if not no_vacuum:261        db.vacuum()262@cli.command(name="rebuild-fts")263@click.argument(264    "path",265    type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),266    required=True,267)268@click.argument("tables", nargs=-1)269@load_extension_option270def rebuild_fts(path, tables, load_extension):271    """Rebuild all or specific full-text search tables"""272    db = sqlite_utils.Database(path)273    _load_extensions(db, load_extension)274    if not tables:275        tables = db.table_names(fts4=True) + db.table_names(fts5=True)276    with db.conn:277        for table in tables:278            db[table].rebuild_fts()279@cli.command()280@click.argument(281    "path",282    type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),283    required=True,284)285def vacuum(path):286    """Run VACUUM against the database"""287    sqlite_utils.Database(path).vacuum()288@cli.command()289@click.argument(290    "path",291    type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),292    required=True,293)294@load_extension_option295def dump(path, load_extension):296    """Output a SQL dump of the schema and full contents of the database"""297    db = sqlite_utils.Database(path)298    _load_extensions(db, load_extension)299    for line in db.conn.iterdump():300        click.echo(line)301@cli.command(name="add-column")302@click.argument(303    "path",304    type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),305    required=True,306)307@click.argument("table")308@click.argument("col_name")309@click.argument(310    "col_type",311    type=click.Choice(312        ["integer", "float", "blob", "text", "INTEGER", "FLOAT", "BLOB", "TEXT"]313    ),314    required=False,315)316@click.option(317    "--fk", type=str, required=False, help="Table to reference as a foreign key"318)319@click.option(320    "--fk-col",321    type=str,322    required=False,323    help="Referenced column on that foreign key table - if omitted will automatically use the primary key",324)325@click.option(326    "--not-null-default",327    type=str,328    required=False,329    help="Add NOT NULL DEFAULT 'TEXT' constraint",330)331@load_extension_option332def add_column(333    path, table, col_name, col_type, fk, fk_col, not_null_default, load_extension334):335    "Add a column to the specified table"336    db = sqlite_utils.Database(path)337    _load_extensions(db, load_extension)338    db[table].add_column(339        col_name, col_type, fk=fk, fk_col=fk_col, not_null_default=not_null_default340    )341@cli.command(name="add-foreign-key")342@click.argument(343    "path",344    type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),345    required=True,346)347@click.argument("table")348@click.argument("column")349@click.argument("other_table", required=False)350@click.argument("other_column", required=False)351@click.option(352    "--ignore",353    is_flag=True,354    help="If foreign key already exists, do nothing",355)356@load_extension_option357def add_foreign_key(358    path, table, column, other_table, other_column, ignore, load_extension359):360    """361    Add a new foreign key constraint to an existing table. Example usage:362        $ sqlite-utils add-foreign-key my.db books author_id authors id363    WARNING: Could corrupt your database! Back up your database file first.364    """365    db = sqlite_utils.Database(path)366    _load_extensions(db, load_extension)367    try:368        db[table].add_foreign_key(column, other_table, other_column, ignore=ignore)369    except AlterError as e:370        raise click.ClickException(e)371@cli.command(name="add-foreign-keys")372@click.argument(373    "path",374    type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),375    required=True,376)377@click.argument("foreign_key", nargs=-1)378@load_extension_option379def add_foreign_keys(path, foreign_key, load_extension):380    """381    Add multiple new foreign key constraints to a database. Example usage:382    \b383    sqlite-utils add-foreign-keys my.db \\384        books author_id authors id \\385        authors country_id countries id386    """387    db = sqlite_utils.Database(path)388    _load_extensions(db, load_extension)389    if len(foreign_key) % 4 != 0:390        raise click.ClickException(391            "Each foreign key requires four values: table, column, other_table, other_column"392        )393    tuples = []394    for i in range(len(foreign_key) // 4):395        tuples.append(tuple(foreign_key[i * 4 : (i * 4) + 4]))396    try:397        db.add_foreign_keys(tuples)398    except AlterError as e:399        raise click.ClickException(e)400@cli.command(name="index-foreign-keys")401@click.argument(402    "path",403    type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),404    required=True,405)406@load_extension_option407def index_foreign_keys(path, load_extension):408    """409    Ensure every foreign key column has an index on it.410    """411    db = sqlite_utils.Database(path)412    _load_extensions(db, load_extension)413    db.index_foreign_keys()414@cli.command(name="create-index")415@click.argument(416    "path",417    type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),418    required=True,419)420@click.argument("table")421@click.argument("column", nargs=-1, required=True)422@click.option("--name", help="Explicit name for the new index")423@click.option("--unique", help="Make this a unique index", default=False, is_flag=True)424@click.option(425    "--if-not-exists",426    help="Ignore if index already exists",427    default=False,428    is_flag=True,429)430@load_extension_option431def create_index(path, table, column, name, unique, if_not_exists, load_extension):432    """433    Add an index to the specified table covering the specified columns.434    Use "sqlite-utils create-index mydb -- -column" to specify descending435    order for a column.436    """437    db = sqlite_utils.Database(path)438    _load_extensions(db, load_extension)439    # Treat -prefix as descending for columns440    columns = []441    for col in column:442        if col.startswith("-"):443            col = DescIndex(col[1:])444        columns.append(col)445    db[table].create_index(446        columns, index_name=name, unique=unique, if_not_exists=if_not_exists447    )448@cli.command(name="enable-fts")449@click.argument(450    "path",451    type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),452    required=True,453)454@click.argument("table")455@click.argument("column", nargs=-1, required=True)456@click.option("--fts4", help="Use FTS4", default=False, is_flag=True)457@click.option("--fts5", help="Use FTS5", default=False, is_flag=True)458@click.option("--tokenize", help="Tokenizer to use, e.g. porter")459@click.option(460    "--create-triggers",461    help="Create triggers to update the FTS tables when the parent table changes.",462    default=False,463    is_flag=True,464)465@load_extension_option466def enable_fts(467    path, table, column, fts4, fts5, tokenize, create_triggers, load_extension468):469    "Enable full-text search for specific table and columns"470    fts_version = "FTS5"471    if fts4 and fts5:472        click.echo("Can only use one of --fts4 or --fts5", err=True)473        return474    elif fts4:475        fts_version = "FTS4"476    db = sqlite_utils.Database(path)477    _load_extensions(db, load_extension)478    db[table].enable_fts(479        column,480        fts_version=fts_version,481        tokenize=tokenize,482        create_triggers=create_triggers,483    )484@cli.command(name="populate-fts")485@click.argument(486    "path",487    type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),488    required=True,489)490@click.argument("table")491@click.argument("column", nargs=-1, required=True)492@load_extension_option493def populate_fts(path, table, column, load_extension):494    "Re-populate full-text search for specific table and columns"495    db = sqlite_utils.Database(path)496    _load_extensions(db, load_extension)497    db[table].populate_fts(column)498@cli.command(name="disable-fts")499@click.argument(500    "path",501    type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),502    required=True,503)504@click.argument("table")505@load_extension_option506def disable_fts(path, table, load_extension):507    "Disable full-text search for specific table"508    db = sqlite_utils.Database(path)509    _load_extensions(db, load_extension)510    db[table].disable_fts()511@cli.command(name="enable-wal")512@click.argument(513    "path",514    nargs=-1,515    type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),516    required=True,517)518@load_extension_option519def enable_wal(path, load_extension):520    "Enable WAL for database files"521    for path_ in path:522        db = sqlite_utils.Database(path_)523        _load_extensions(db, load_extension)524        db.enable_wal()525@cli.command(name="disable-wal")526@click.argument(527    "path",528    nargs=-1,529    type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),530    required=True,531)532@load_extension_option533def disable_wal(path, load_extension):534    "Disable WAL for database files"535    for path_ in path:536        db = sqlite_utils.Database(path_)537        _load_extensions(db, load_extension)538        db.disable_wal()539@cli.command(name="enable-counts")540@click.argument(541    "path",542    type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),543    required=True,544)545@click.argument("tables", nargs=-1)546@load_extension_option547def enable_counts(path, tables, load_extension):548    "Configure triggers to update a _counts table with row counts"549    db = sqlite_utils.Database(path)550    _load_extensions(db, load_extension)551    if not tables:552        db.enable_counts()553    else:554        # Check all tables exist555        bad_tables = [table for table in tables if not db[table].exists()]556        if bad_tables:557            raise click.ClickException("Invalid tables: {}".format(bad_tables))558        for table in tables:559            db[table].enable_counts()560@cli.command(name="reset-counts")561@click.argument(562    "path",563    type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False),564    required=True,565)566@load_extension_option567def reset_counts(path, load_extension):568    "Reset calculated counts in the _counts table"569    db = sqlite_utils.Database(path)570    _load_extensions(db, load_extension)571    db.reset_counts()572def insert_upsert_options(fn):573    for decorator in reversed(574        (575            click.argument(576                "path",577                type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),578                required=True,579            ),580            click.argument("table"),581            click.argument("json_file", type=click.File("rb"), required=True),582            click.option(583                "--pk", help="Columns to use as the primary key, e.g. id", multiple=True584            ),585            click.option("--nl", is_flag=True, help="Expect newline-delimited JSON"),586            click.option("--flatten", is_flag=True, help="Flatten nested JSON objects"),587            click.option("-c", "--csv", is_flag=True, help="Expect CSV"),588            click.option("--tsv", is_flag=True, help="Expect TSV"),589            click.option("--delimiter", help="Delimiter to use for CSV files"),590            click.option("--quotechar", help="Quote character to use for CSV/TSV"),591            click.option(592                "--sniff", is_flag=True, help="Detect delimiter and quote character"593            ),594            click.option(595                "--no-headers", is_flag=True, help="CSV file has no header row"596            ),597            click.option(598                "--batch-size", type=int, default=100, help="Commit every X records"599            ),600            click.option(601                "--alter",602                is_flag=True,603                help="Alter existing table to add any missing columns",604            ),605            click.option(606                "--not-null",607                multiple=True,608                help="Columns that should be created as NOT NULL",609            ),610            click.option(611                "--default",612                multiple=True,613                type=(str, str),614                help="Default value that should be set for a column",615            ),616            click.option(617                "--encoding",618                help="Character encoding for input, defaults to utf-8",619            ),620            click.option(621                "-d",622                "--detect-types",623                is_flag=True,624                envvar="SQLITE_UTILS_DETECT_TYPES",625                help="Detect types for columns in CSV/TSV data",626            ),627            load_extension_option,628            click.option("--silent", is_flag=True, help="Do not show progress bar"),629        )630    ):631        fn = decorator(fn)632    return fn633def insert_upsert_implementation(634    path,635    table,636    json_file,637    pk,638    nl,639    flatten,640    csv,641    tsv,642    delimiter,643    quotechar,644    sniff,645    no_headers,646    batch_size,647    alter,648    upsert,649    ignore=False,650    replace=False,651    truncate=False,652    not_null=None,653    default=None,654    encoding=None,655    detect_types=None,656    load_extension=None,657    silent=False,658):659    db = sqlite_utils.Database(path)660    _load_extensions(db, load_extension)661    if (delimiter or quotechar or sniff or no_headers) and not tsv:662        csv = True663    if (nl + csv + tsv) >= 2:664        raise click.ClickException("Use just one of --nl, --csv or --tsv")665    if (csv or tsv) and flatten:666        raise click.ClickException("--flatten cannot be used with --csv or --tsv")667    if encoding and not (csv or tsv):668        raise click.ClickException("--encoding must be used with --csv or --tsv")669    if pk and len(pk) == 1:670        pk = pk[0]671    encoding = encoding or "utf-8-sig"672    buffered = io.BufferedReader(json_file, buffer_size=4096)673    decoded = io.TextIOWrapper(buffered, encoding=encoding)674    tracker = None675    if csv or tsv:676        if sniff:677            # Read first 2048 bytes and use that to detect678            first_bytes = buffered.peek(2048)679            dialect = csv_std.Sniffer().sniff(first_bytes.decode(encoding, "ignore"))680        else:681            dialect = "excel-tab" if tsv else "excel"682        with file_progress(decoded, silent=silent) as decoded:683            csv_reader_args = {"dialect": dialect}684            if delimiter:685                csv_reader_args["delimiter"] = delimiter686            if quotechar:687                csv_reader_args["quotechar"] = quotechar688            reader = csv_std.reader(decoded, **csv_reader_args)689            first_row = next(reader)690            if no_headers:691                headers = ["untitled_{}".format(i + 1) for i in range(len(first_row))]692                reader = itertools.chain([first_row], reader)693            else:694                headers = first_row695            docs = (dict(zip(headers, row)) for row in reader)696            if detect_types:697                tracker = TypeTracker()698                docs = tracker.wrap(docs)699    else:700        try:701            if nl:702                docs = (json.loads(line) for line in decoded)703            else:704                docs = json.load(decoded)705                if isinstance(docs, dict):706                    docs = [docs]707        except json.decoder.JSONDecodeError:708            raise click.ClickException(709                "Invalid JSON - use --csv for CSV or --tsv for TSV files"710            )711        if flatten:712            docs = (dict(_flatten(doc)) for doc in docs)713    extra_kwargs = {"ignore": ignore, "replace": replace, "truncate": truncate}714    if not_null:715        extra_kwargs["not_null"] = set(not_null)716    if default:717        extra_kwargs["defaults"] = dict(default)718    if upsert:719        extra_kwargs["upsert"] = upsert720    # Apply {"$base64": true, ...} decoding, if needed721    docs = (decode_base64_values(doc) for doc in docs)722    try:723        db[table].insert_all(724            docs, pk=pk, batch_size=batch_size, alter=alter, **extra_kwargs725        )726    except Exception as e:727        if (728            isinstance(e, sqlite3.OperationalError)729            and e.args730            and "has no column named" in e.args[0]731        ):732            raise click.ClickException(733                "{}\n\nTry using --alter to add additional columns".format(e.args[0])734            )735        # If we can find sql= and parameters= arguments, show those736        variables = _find_variables(e.__traceback__, ["sql", "parameters"])737        if "sql" in variables and "parameters" in variables:738            raise click.ClickException(739                "{}\n\nsql = {}\nparameters = {}".format(740                    str(e), variables["sql"], variables["parameters"]741                )742            )743        else:744            raise745    if tracker is not None:746        db[table].transform(types=tracker.types)747def _flatten(d):748    for key, value in d.items():749        if isinstance(value, dict):750            for key2, value2 in _flatten(value):751                yield key + "_" + key2, value2752        else:753            yield key, value754def _find_variables(tb, vars):755    to_find = list(vars)756    found = {}757    for var in to_find:758        if var in tb.tb_frame.f_locals:759            vars.remove(var)760            found[var] = tb.tb_frame.f_locals[var]761    if vars and tb.tb_next:762        found.update(_find_variables(tb.tb_next, vars))763    return found764@cli.command()765@insert_upsert_options766@click.option(767    "--ignore", is_flag=True, default=False, help="Ignore records if pk already exists"768)769@click.option(770    "--replace",771    is_flag=True,772    default=False,773    help="Replace records if pk already exists",774)775@click.option(776    "--truncate",777    is_flag=True,778    default=False,779    help="Truncate table before inserting records, if table already exists",780)781def insert(782    path,783    table,784    json_file,785    pk,786    nl,787    flatten,788    csv,789    tsv,790    delimiter,791    quotechar,792    sniff,793    no_headers,794    batch_size,795    alter,796    encoding,797    detect_types,798    load_extension,799    silent,800    ignore,801    replace,802    truncate,803    not_null,804    default,805):806    """807    Insert records from JSON file into a table, creating the table if it808    does not already exist.809    Input should be a JSON array of objects, unless --nl or --csv is used.810    """811    try:812        insert_upsert_implementation(813            path,814            table,815            json_file,816            pk,817            nl,818            flatten,819            csv,820            tsv,821            delimiter,822            quotechar,823            sniff,824            no_headers,825            batch_size,826            alter=alter,827            upsert=False,828            ignore=ignore,829            replace=replace,830            truncate=truncate,831            encoding=encoding,832            detect_types=detect_types,833            load_extension=load_extension,834            silent=silent,835            not_null=not_null,836            default=default,837        )838    except UnicodeDecodeError as ex:839        raise click.ClickException(UNICODE_ERROR.format(ex))840@cli.command()841@insert_upsert_options842def upsert(843    path,844    table,845    json_file,846    pk,847    nl,848    flatten,849    csv,850    tsv,851    batch_size,852    delimiter,853    quotechar,854    sniff,855    no_headers,856    alter,857    not_null,858    default,859    encoding,860    detect_types,861    load_extension,862    silent,863):864    """865    Upsert records based on their primary key. Works like 'insert' but if866    an incoming record has a primary key that matches an existing record867    the existing record will be updated.868    """869    try:870        insert_upsert_implementation(871            path,872            table,873            json_file,874            pk,875            nl,876            flatten,877            csv,878            tsv,879            delimiter,880            quotechar,881            sniff,882            no_headers,883            batch_size,884            alter=alter,885            upsert=True,886            not_null=not_null,887            default=default,888            encoding=encoding,889            load_extension=load_extension,890            silent=silent,891        )892    except UnicodeDecodeError as ex:893        raise click.ClickException(UNICODE_ERROR.format(ex))894@cli.command(name="create-table")895@click.argument(896    "path",897    type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),898    required=True,899)900@click.argument("table")901@click.argument("columns", nargs=-1, required=True)902@click.option("--pk", help="Column to use as primary key")903@click.option(904    "--not-null",905    multiple=True,906    help="Columns that should be created as NOT NULL",907)908@click.option(909    "--default",910    multiple=True,911    type=(str, str),912    help="Default value that should be set for a column",913)914@click.option(915    "--fk",916    multiple=True,917    type=(str, str, str),918    help="Column, other table, other column to set as a foreign key",919)920@click.option(921    "--ignore",922    is_flag=True,923    help="If table already exists, do nothing",924)925@click.option(926    "--replace",927    is_flag=True,928    help="If table already exists, replace it",929)930@load_extension_option931def create_table(932    path, table, columns, pk, not_null, default, fk, ignore, replace, load_extension933):934    """935    Add a table with the specified columns. Columns should be specified using936    name, type pairs, for example:937    \b938    sqlite-utils create-table my.db people \\939        id integer \\940        name text \\941        height float \\942        photo blob --pk id943    """944    db = sqlite_utils.Database(path)945    _load_extensions(db, load_extension)946    if len(columns) % 2 == 1:947        raise click.ClickException(948            "columns must be an even number of 'name' 'type' pairs"949        )950    coltypes = {}951    columns = list(columns)952    while columns:953        name = columns.pop(0)954        ctype = columns.pop(0)955        if ctype.upper() not in VALID_COLUMN_TYPES:956            raise click.ClickException(957                "column types must be one of {}".format(VALID_COLUMN_TYPES)958            )959        coltypes[name] = ctype.upper()960    # Does table already exist?961    if table in db.table_names():962        if ignore:963            return964        elif replace:965            db[table].drop()966        else:967            raise click.ClickException(968                'Table "{}" already exists. Use --replace to delete and replace it.'.format(969                    table970                )971            )972    db[table].create(973        coltypes, pk=pk, not_null=not_null, defaults=dict(default), foreign_keys=fk974    )975@cli.command(name="drop-table")976@click.argument(977    "path",978    type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),979    required=True,980)981@click.argument("table")982@click.option("--ignore", is_flag=True)983@load_extension_option984def drop_table(path, table, ignore, load_extension):985    "Drop the specified table"986    db = sqlite_utils.Database(path)987    _load_extensions(db, load_extension)988    try:989        db[table].drop(ignore=ignore)990    except sqlite3.OperationalError:991        raise click.ClickException('Table "{}" does not exist'.format(table))992@cli.command(name="create-view")993@click.argument(994    "path",995    type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),996    required=True,997)998@click.argument("view")999@click.argument("select")1000@click.option(1001    "--ignore",1002    is_flag=True,1003    help="If view already exists, do nothing",1004)1005@click.option(1006    "--replace",1007    is_flag=True,1008    help="If view already exists, replace it",1009)1010@load_extension_option1011def create_view(path, view, select, ignore, replace, load_extension):1012    "Create a view for the provided SELECT query"1013    db = sqlite_utils.Database(path)1014    _load_extensions(db, load_extension)1015    # Does view already exist?1016    if view in db.view_names():1017        if ignore:1018            return1019        elif replace:1020            db[view].drop()1021        else:1022            raise click.ClickException(1023                'View "{}" already exists. Use --replace to delete and replace it.'.format(1024                    view1025                )1026            )1027    db.create_view(view, select)1028@cli.command(name="drop-view")1029@click.argument(1030    "path",1031    type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),1032    required=True,1033)1034@click.argument("view")1035@click.option("--ignore", is_flag=True)1036@load_extension_option1037def drop_view(path, view, ignore, load_extension):1038    "Drop the specified view"1039    db = sqlite_utils.Database(path)1040    _load_extensions(db, load_extension)1041    try:1042        db[view].drop(ignore=ignore)1043    except sqlite3.OperationalError:1044        raise click.ClickException('View "{}" does not exist'.format(view))1045@cli.command()1046@click.argument(1047    "path",1048    type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),1049    required=True,1050)1051@click.argument("sql")1052@click.option(1053    "--attach",1054    type=(str, click.Path(file_okay=True, dir_okay=False, allow_dash=False)),1055    multiple=True,1056    help="Additional databases to attach - specify alias and filepath",1057)1058@output_options1059@click.option("-r", "--raw", is_flag=True, help="Raw output, first column of first row")1060@click.option(1061    "-p",1062    "--param",1063    multiple=True,1064    type=(str, str),1065    help="Named :parameters for SQL query",1066)1067@load_extension_option1068def query(1069    path,1070    sql,1071    attach,1072    nl,1073    arrays,1074    csv,1075    tsv,1076    no_headers,1077    table,1078    fmt,1079    json_cols,1080    raw,1081    param,1082    load_extension,1083):1084    "Execute SQL query and return the results as JSON"1085    db = sqlite_utils.Database(path)1086    for alias, attach_path in attach:1087        db.attach(alias, attach_path)1088    _load_extensions(db, load_extension)1089    db.register_fts4_bm25()1090    _execute_query(1091        db, sql, param, raw, table, csv, tsv, no_headers, fmt, nl, arrays, json_cols1092    )1093@cli.command()1094@click.argument(1095    "paths",1096    type=click.Path(file_okay=True, dir_okay=False, allow_dash=True),1097    required=False,1098    nargs=-1,1099)1100@click.argument("sql")1101@click.option(1102    "--attach",1103    type=(str, click.Path(file_okay=True, dir_okay=False, allow_dash=False)),1104    multiple=True,1105    help="Additional databases to attach - specify alias and filepath",1106)1107@output_options1108@click.option("-r", "--raw", is_flag=True, help="Raw output, first column of first row")1109@click.option(1110    "-p",1111    "--param",1112    multiple=True,1113    type=(str, str),1114    help="Named :parameters for SQL query",1115)1116@click.option(1117    "--encoding",1118    help="Character encoding for CSV input, defaults to utf-8",1119)1120@click.option(1121    "-n",1122    "--no-detect-types",1123    is_flag=True,1124    help="Treat all CSV/TSV columns as TEXT",1125)1126@click.option("--schema", is_flag=True, help="Show SQL schema for in-memory database")1127@click.option("--dump", is_flag=True, help="Dump SQL for in-memory database")1128@click.option(1129    "--save",1130    type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),1131    help="Save in-memory database to this file",1132)1133@click.option(1134    "--analyze",1135    is_flag=True,1136    help="Analyze resulting tables and output results",1137)1138@load_extension_option1139def memory(1140    paths,1141    sql,1142    attach,1143    nl,1144    arrays,1145    csv,1146    tsv,1147    no_headers,1148    table,1149    fmt,1150    json_cols,1151    raw,1152    param,1153    encoding,1154    no_detect_types,1155    schema,1156    dump,1157    save,1158    analyze,1159    load_extension,1160):1161    """Execute SQL query against an in-memory database, optionally populated by imported data1162    To import data from CSV, TSV or JSON files pass them on the command-line:1163    \b1164        sqlite-utils memory one.csv two.json \\1165            "select * from one join two on one.two_id = two.id"1166    For data piped into the tool from standard input, use "-" or "stdin":1167    \b1168        cat animals.csv | sqlite-utils memory - \\1169            "select * from stdin where species = 'dog'"1170    The format of the data will be automatically detected. You can specify the format1171    explicitly using :json, :csv, :tsv or :nl (for newline-delimited JSON) - for example:1172    \b1173        cat animals.csv | sqlite-utils memory stdin:csv places.dat:nl \\1174            "select * from stdin where place_id in (select id from places)"1175    Use --schema to view the SQL schema of any imported files:1176    \b1177        sqlite-utils memory animals.csv --schema1178    """1179    db = sqlite_utils.Database(memory=True)1180    # If --dump or --save or --analyze used but no paths detected, assume SQL query is a path:1181    if (dump or save or schema or analyze) and not paths:1182        paths = [sql]1183        sql = None1184    for i, path in enumerate(paths):1185        # Path may have a :format suffix1186        if ":" in path and path.rsplit(":", 1)[-1].upper() in Format.__members__:1187            path, suffix = path.rsplit(":", 1)1188            format = Format[suffix.upper()]1189        else:1190            format = None1191        if path in ("-", "stdin"):1192            csv_fp = sys.stdin.buffer1193            csv_table = "stdin"1194        else:1195            csv_path = pathlib.Path(path)1196            csv_table = csv_path.stem1197            csv_fp = csv_path.open("rb")1198        rows, format_used = rows_from_file(csv_fp, format=format, encoding=encoding)1199        tracker = None1200        if format_used in (Format.CSV, Format.TSV) and not no_detect_types:1201            tracker = TypeTracker()1202            rows = tracker.wrap(rows)1203        db[csv_table].insert_all(rows, alter=True)1204        if tracker is not None:1205            db[csv_table].transform(types=tracker.types)1206        # Add convenient t / t1 / t2 views1207        view_names = ["t{}".format(i + 1)]1208        if i == 0:1209            view_names.append("t")1210        for view_name in view_names:1211            if not db[view_name].exists():1212                db.create_view(view_name, "select * from [{}]".format(csv_table))1213    if analyze:1214        _analyze(db, tables=None, columns=None, save=False)1215        return1216    if dump:1217        for line in db.conn.iterdump():1218            click.echo(line)1219        return1220    if schema:1221        click.echo(db.schema)1222        return1223    if save:1224        db2 = sqlite_utils.Database(save)1225        for line in db.conn.iterdump():1226            db2.execute(line)1227        return1228    for alias, attach_path in attach:1229        db.attach(alias, attach_path)1230    _load_extensions(db, load_extension)1231    db.register_fts4_bm25()1232    _execute_query(1233        db, sql, param, raw, table, csv, tsv, no_headers, fmt, nl, arrays, json_cols1234    )1235def _execute_query(1236    db, sql, param, raw, table, csv, tsv, no_headers, fmt, nl, arrays, json_cols1237):1238    with db.conn:1239        try:1240            cursor = db.execute(sql, dict(param))1241        except sqlite3.OperationalError as e:1242            raise click.ClickException(str(e))1243        if cursor.description is None:1244            # This was an update/insert1245            headers = ["rows_affected"]1246            cursor = [[cursor.rowcount]]1247        else:1248            headers = [c[0] for c in cursor.description]1249        if raw:1250            data = cursor.fetchone()[0]1251            if isinstance(data, bytes):1252                sys.stdout.buffer.write(data)1253            else:1254                sys.stdout.write(str(data))1255        elif table:1256            print(tabulate.tabulate(list(cursor), headers=headers, tablefmt=fmt))1257        elif csv or tsv:1258            writer = csv_std.writer(sys.stdout, dialect="excel-tab" if tsv else "excel")1259            if not no_headers:1260                writer.writerow(headers)1261            for row in cursor:1262                writer.writerow(row)1263        else:1264            for line in output_rows(cursor, headers, nl, arrays, json_cols):1265                click.echo(line)1266@cli.command()1267@click.argument(1268    "path",1269    type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),1270    required=True,1271)1272@click.argument("dbtable")1273@click.argument("q")1274@click.option("-o", "--order", type=str, help="Order by ('column' or 'column desc')")1275@click.option("-c", "--column", type=str, multiple=True, help="Columns to return")1276@click.option(1277    "--limit",1278    type=int,1279    help="Number of rows to return - defaults to everything",1280)1281@click.option(1282    "--sql", "show_sql", is_flag=True, help="Show SQL query that would be run"1283)1284@click.option("--quote", is_flag=True, help="Apply FTS quoting rules to search term")1285@output_options1286@load_extension_option1287@click.pass_context1288def search(1289    ctx,1290    path,1291    dbtable,1292    q,1293    order,1294    show_sql,1295    quote,1296    column,1297    limit,1298    nl,1299    arrays,1300    csv,1301    tsv,1302    no_headers,1303    table,1304    fmt,1305    json_cols,1306    load_extension,1307):1308    "Execute a full-text search against this table"1309    db = sqlite_utils.Database(path)1310    _load_extensions(db, load_extension)1311    # Check table exists1312    table_obj = db[dbtable]1313    if not table_obj.exists():1314        raise click.ClickException("Table '{}' does not exist".format(dbtable))1315    if not table_obj.detect_fts():1316        raise click.ClickException(1317            "Table '{}' is not configured for full-text search".format(dbtable)1318        )1319    if column:1320        # Check they all exist1321        table_columns = table_obj.columns_dict1322        for c in column:1323            if c not in table_columns:1324                raise click.ClickException(1325                    "Table '{}' has no column '{}".format(dbtable, c)1326                )1327    sql = table_obj.search_sql(columns=column, order_by=order, limit=limit)1328    if show_sql:1329        click.echo(sql)1330        return1331    if quote:1332        q = db.quote_fts(q)1333    try:1334        ctx.invoke(1335            query,1336            path=path,1337            sql=sql,1338            nl=nl,1339            arrays=arrays,1340            csv=csv,1341            tsv=tsv,1342            no_headers=no_headers,1343            table=table,1344            fmt=fmt,1345            json_cols=json_cols,1346            param=[("query", q)],1347            load_extension=load_extension,1348        )1349    except click.ClickException as e:1350        if "malformed MATCH expression" in str(e) or "unterminated string" in str(e):1351            raise click.ClickException(1352                "{}\n\nTry running this again with the --quote option".format(str(e))1353            )1354        else:1355            raise1356@cli.command()1357@click.argument(1358    "path",1359    type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),1360    required=True,1361)1362@click.argument("dbtable")1363@click.option("-c", "--column", type=str, multiple=True, help="Columns to return")1364@output_options1365@load_extension_option1366@click.pass_context1367def rows(1368    ctx,1369    path,1370    dbtable,1371    column,1372    nl,1373    arrays,1374    csv,1375    tsv,1376    no_headers,1377    table,1378    fmt,1379    json_cols,1380    load_extension,1381):1382    "Output all rows in the specified table"1383    columns = "*"1384    if column:1385        columns = ", ".join("[{}]".format(c) for c in column)1386    ctx.invoke(1387        query,1388        path=path,1389        sql="select {} from [{}]".format(columns, dbtable),1390        nl=nl,1391        arrays=arrays,1392        csv=csv,1393        tsv=tsv,1394        no_headers=no_headers,1395        table=table,1396        fmt=fmt,1397        json_cols=json_cols,1398        load_extension=load_extension,1399    )1400@cli.command()1401@click.argument(1402    "path",1403    type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),1404    required=True,1405)1406@click.argument("tables", nargs=-1)1407@output_options1408@load_extension_option1409@click.pass_context1410def triggers(1411    ctx,1412    path,1413    tables,1414    nl,1415    arrays,1416    csv,1417    tsv,1418    no_headers,1419    table,1420    fmt,1421    json_cols,1422    load_extension,1423):1424    "Show triggers configured in this database"1425    sql = "select name, tbl_name as [table], sql from sqlite_master where type = 'trigger'"1426    if tables:1427        quote = sqlite_utils.Database(memory=True).quote1428        sql += " and [table] in ({})".format(1429            ", ".join(quote(table) for table in tables)1430        )1431    ctx.invoke(1432        query,1433        path=path,1434        sql=sql,1435        nl=nl,1436        arrays=arrays,1437        csv=csv,1438        tsv=tsv,1439        no_headers=no_headers,1440        table=table,1441        fmt=fmt,1442        json_cols=json_cols,1443        load_extension=load_extension,1444    )1445@cli.command()1446@click.argument(1447    "path",1448    type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),1449    required=True,1450)1451@click.argument("tables", nargs=-1)1452@click.option("--aux", is_flag=True, help="Include auxiliary columns")1453@output_options1454@load_extension_option1455@click.pass_context1456def indexes(1457    ctx,1458    path,1459    tables,1460    aux,1461    nl,1462    arrays,1463    csv,1464    tsv,1465    no_headers,1466    table,1467    fmt,1468    json_cols,1469    load_extension,1470):1471    "Show indexes for this database"1472    sql = """1473    select1474      sqlite_master.name as "table",1475      indexes.name as index_name,1476      xinfo.*1477    from sqlite_master1478      join pragma_index_list(sqlite_master.name) indexes1479      join pragma_index_xinfo(index_name) xinfo1480    where1481      sqlite_master.type = 'table'1482    """1483    if tables:1484        quote = sqlite_utils.Database(memory=True).quote1485        sql += " and sqlite_master.name in ({})".format(1486            ", ".join(quote(table) for table in tables)1487        )1488    if not aux:1489        sql += " and xinfo.key = 1"1490    ctx.invoke(1491        query,1492        path=path,1493        sql=sql,1494        nl=nl,1495        arrays=arrays,1496        csv=csv,1497        tsv=tsv,1498        no_headers=no_headers,1499        table=table,1500        fmt=fmt,1501        json_cols=json_cols,1502        load_extension=load_extension,1503    )1504@cli.command()1505@click.argument(1506    "path",1507    type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),1508    required=True,1509)1510@click.argument("tables", nargs=-1, required=False)1511@load_extension_option1512def schema(1513    path,1514    tables,1515    load_extension,1516):1517    "Show full schema for this database or for specified tables"1518    db = sqlite_utils.Database(path)1519    _load_extensions(db, load_extension)1520    if tables:1521        for table in tables:1522            click.echo(db[table].schema)1523    else:1524        click.echo(db.schema)1525@cli.command()1526@click.argument(1527    "path",1528    type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),1529    required=True,1530)1531@click.argument("table")1532@click.option(1533    "--type",1534    type=(1535        str,1536        click.Choice(["INTEGER", "TEXT", "FLOAT", "BLOB"], case_sensitive=False),1537    ),1538    multiple=True,1539    help="Change column type to INTEGER, TEXT, FLOAT or BLOB",1540)1541@click.option("--drop", type=str, multiple=True, help="Drop this column")1542@click.option(1543    "--rename", type=(str, str), multiple=True, help="Rename this column to X"1544)1545@click.option("-o", "--column-order", type=str, multiple=True, help="Reorder columns")1546@click.option("--not-null", type=str, multiple=True, help="Set this column to NOT NULL")1547@click.option(1548    "--not-null-false", type=str, multiple=True, help="Remove NOT NULL from this column"1549)1550@click.option("--pk", type=str, multiple=True, help="Make this column the primary key")1551@click.option(1552    "--pk-none", is_flag=True, help="Remove primary key (convert to rowid table)"1553)1554@click.option(1555    "--default",1556    type=(str, str),1557    multiple=True,1558    help="Set default value for this column",1559)1560@click.option(1561    "--default-none", type=str, multiple=True, help="Remove default from this column"1562)1563@click.option(1564    "--drop-foreign-key",1565    type=str,1566    multiple=True,1567    help="Drop this foreign key constraint",1568)1569@click.option("--sql", is_flag=True, help="Output SQL without executing it")1570@load_extension_option1571def transform(1572    path,1573    table,1574    type,1575    drop,1576    rename,1577    column_order,1578    not_null,1579    not_null_false,1580    pk,1581    pk_none,1582    default,1583    default_none,1584    drop_foreign_key,1585    sql,1586    load_extension,1587):1588    "Transform a table beyond the capabilities of ALTER TABLE"1589    db = sqlite_utils.Database(path)1590    _load_extensions(db, load_extension)1591    types = {}1592    kwargs = {}1593    for column, ctype in type:1594        if ctype.upper() not in VALID_COLUMN_TYPES:1595            raise click.ClickException(1596                "column types must be one of {}".format(VALID_COLUMN_TYPES)1597            )1598        types[column] = ctype.upper()1599    not_null_dict = {}1600    for column in not_null:1601        not_null_dict[column] = True1602    for column in not_null_false:1603        not_null_dict[column] = False1604    default_dict = {}1605    for column, value in default:1606        default_dict[column] = value1607    for column in default_none:1608        default_dict[column] = None1609    kwargs["types"] = types1610    kwargs["drop"] = set(drop)1611    kwargs["rename"] = dict(rename)1612    kwargs["column_order"] = column_order or None1613    kwargs["not_null"] = not_null_dict1614    if pk:1615        if len(pk) == 1:1616            kwargs["pk"] = pk[0]1617        else:1618            kwargs["pk"] = pk1619    elif pk_none:1620        kwargs["pk"] = None1621    kwargs["defaults"] = default_dict1622    if drop_foreign_key:1623        kwargs["drop_foreign_keys"] = drop_foreign_key1624    if sql:1625        for line in db[table].transform_sql(**kwargs):1626            click.echo(line)1627    else:1628        db[table].transform(**kwargs)1629@cli.command()1630@click.argument(1631    "path",1632    type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),1633    required=True,1634)1635@click.argument("table")1636@click.argument("columns", nargs=-1, required=True)1637@click.option(1638    "--table", "other_table", help="Name of the other table to extract columns to"1639)1640@click.option("--fk-column", help="Name of the foreign key column to add to the table")1641@click.option(1642    "--rename",1643    type=(str, str),1644    multiple=True,1645    help="Rename this column in extracted table",1646)1647@load_extension_option1648def extract(1649    path,1650    table,1651    columns,1652    other_table,1653    fk_column,1654    rename,1655    load_extension,1656):1657    "Extract one or more columns into a separate table"1658    db = sqlite_utils.Database(path)1659    _load_extensions(db, load_extension)1660    kwargs = dict(1661        columns=columns,1662        table=other_table,1663        fk_column=fk_column,1664        rename=dict(rename),1665    )1666    db[table].extract(**kwargs)1667@cli.command(name="insert-files")1668@click.argument(1669    "path",1670    type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),1671    required=True,1672)1673@click.argument("table")1674@click.argument(1675    "file_or_dir",1676    nargs=-1,1677    required=True,1678    type=click.Path(file_okay=True, dir_okay=True, allow_dash=True),1679)1680@click.option(1681    "-c",1682    "--column",1683    type=str,1684    multiple=True,1685    help="Column definitions for the table",1686)1687@click.option("--pk", type=str, help="Column to use as primary key")1688@click.option("--alter", is_flag=True, help="Alter table to add missing columns")1689@click.option("--replace", is_flag=True, help="Replace files with matching primary key")1690@click.option("--upsert", is_flag=True, help="Upsert files with matching primary key")1691@click.option("--name", type=str, help="File name to use")1692@click.option("--text", is_flag=True, help="Store file content as TEXT, not BLOB")1693@click.option(1694    "--encoding",1695    help="Character encoding for input, defaults to utf-8",1696)1697@click.option("-s", "--silent", is_flag=True, help="Don't show a progress bar")1698@load_extension_option1699def insert_files(1700    path,1701    table,1702    file_or_dir,1703    column,1704    pk,1705    alter,1706    replace,1707    upsert,1708    name,1709    text,1710    encoding,1711    silent,1712    load_extension,1713):1714    """1715    Insert one or more files using BLOB columns in the specified table1716    Example usage:1717    \b1718    sqlite-utils insert-files pics.db images *.gif \\1719        -c name:name \\1720        -c content:content \\1721        -c content_hash:sha256 \\1722        -c created:ctime_iso \\1723        -c modified:mtime_iso \\1724        -c size:size \\1725        --pk name1726    """1727    if not column:1728        if text:1729            column = ["path:path", "content_text:content_text", "size:size"]1730        else:1731            column = ["path:path", "content:content", "size:size"]1732        if not pk:1733            pk = "path"1734    def yield_paths_and_relative_paths():1735        for f_or_d in file_or_dir:1736            path = pathlib.Path(f_or_d)1737            if f_or_d == "-":1738                yield "-", "-"1739            elif path.is_dir():1740                for subpath in path.rglob("*"):1741                    if subpath.is_file():1742                        yield subpath, subpath.relative_to(path)1743            elif path.is_file():1744                yield path, path1745    # Load all paths so we can show a progress bar1746    paths_and_relative_paths = list(yield_paths_and_relative_paths())1747    with progressbar(paths_and_relative_paths, silent=silent) as bar:1748        def to_insert():1749            for path, relative_path in bar:1750                row = {}1751                # content_text is special case as it considers 'encoding'1752                def _content_text(p):1753                    resolved = p.resolve()1754                    try:1755                        return resolved.read_text(encoding=encoding)1756                    except UnicodeDecodeError as e:1757                        raise UnicodeDecodeErrorForPath(e, resolved)1758                lookups = dict(FILE_COLUMNS, content_text=_content_text)1759                if path == "-":1760                    stdin_data = sys.stdin.buffer.read()1761                    # We only support a subset of columns for this case1762                    lookups = {1763                        "name": lambda p: name or "-",1764                        "path": lambda p: name or "-",1765                        "content": lambda p: stdin_data,1766                        "content_text": lambda p: stdin_data.decode(1767                            encoding or "utf-8"1768                        ),1769                        "sha256": lambda p: hashlib.sha256(stdin_data).hexdigest(),1770                        "md5": lambda p: hashlib.md5(stdin_data).hexdigest(),1771                        "size": lambda p: len(stdin_data),1772                    }1773                for coldef in column:1774                    if ":" in coldef:1775                        colname, coltype = coldef.rsplit(":", 1)1776                    else:1777                        colname, coltype = coldef, coldef1778                    try:1779                        value = lookups[coltype](path)1780                        row[colname] = value1781                    except KeyError:1782                        raise click.ClickException(1783                            "'{}' is not a valid column definition - options are {}".format(1784                                coltype, ", ".join(lookups.keys())1785                            )1786                        )1787                    # Special case for --name1788                    if coltype == "name" and name:1789                        row[colname] = name1790                yield row1791        db = sqlite_utils.Database(path)1792        _load_extensions(db, load_extension)1793        try:1794            with db.conn:1795                db[table].insert_all(1796                    to_insert(), pk=pk, alter=alter, replace=replace, upsert=upsert1797                )1798        except UnicodeDecodeErrorForPath as e:1799            raise click.ClickException(1800                UNICODE_ERROR.format(1801                    "Could not read file '{}' as text\n\n{}".format(e.path, e.exception)1802                )1803            )1804@cli.command(name="analyze-tables")1805@click.argument(1806    "path",1807    type=click.Path(file_okay=True, dir_okay=False, allow_dash=False, exists=True),1808    required=True,1809)1810@click.argument("tables", nargs=-1)1811@click.option(1812    "-c",1813    "--column",1814    "columns",1815    type=str,1816    multiple=True,1817    help="Specific columns to analyze",1818)1819@click.option("--save", is_flag=True, help="Save results to _analyze_tables table")1820@load_extension_option1821def analyze_tables(1822    path,1823    tables,1824    columns,1825    save,1826    load_extension,1827):1828    "Analyze the columns in one or more tables"1829    db = sqlite_utils.Database(path)1830    _load_extensions(db, load_extension)1831    _analyze(db, tables, columns, save)1832def _analyze(db, tables, columns, save):1833    if not tables:1834        tables = db.table_names()1835    todo = []1836    table_counts = {}1837    for table in tables:1838        table_counts[table] = db[table].count1839        for column in db[table].columns:1840            if not columns or column.name in columns:1841                todo.append((table, column.name))1842    # Now we now how many we need to do1843    for i, (table, column) in enumerate(todo):1844        column_details = db[table].analyze_column(1845            column, total_rows=table_counts[table], value_truncate=801846        )1847        if save:1848            db["_analyze_tables_"].insert(1849                column_details._asdict(), pk=("table", "column"), replace=True1850            )1851        most_common_rendered = _render_common(1852            "\n\n  Most common:", column_details.most_common1853        )1854        least_common_rendered = _render_common(1855            "\n\n  Least common:", column_details.least_common1856        )1857        details = (1858            (1859                textwrap.dedent(1860                    """1861        {table}.{column}: ({i}/{total})1862          Total rows: {total_rows}1863          Null rows: {num_null}1864          Blank rows: {num_blank}1865          Distinct values: {num_distinct}{most_common_rendered}{least_common_rendered}1866        """1867                )1868                .strip()1869                .format(1870                    i=i + 1,1871                    total=len(todo),1872                    most_common_rendered=most_common_rendered,1873                    least_common_rendered=least_common_rendered,1874                    **column_details._asdict()1875                )1876            )1877            + "\n"1878        )1879        click.echo(details)1880def _generate_convert_help():1881    help = textwrap.dedent(1882        """1883    Convert columns using Python code you supply. For example:1884    \b1885    $ sqlite-utils convert my.db mytable mycolumn \\1886        '"\\n".join(textwrap.wrap(value, 10))' \\1887        --import=textwrap1888    "value" is a variable with the column value to be converted.1889    The following common operations are available as recipe functions:1890    """1891    ).strip()1892    recipe_names = [1893        n for n in dir(recipes) if not n.startswith("_") and n not in ("json", "parser")1894    ]1895    for name in recipe_names:1896        fn = getattr(recipes, name)1897        help += "\n\nr.{}{}\n\n  {}".format(1898            name, str(inspect.signature(fn)), fn.__doc__1899        )1900    help += "\n\n"1901    help += textwrap.dedent(1902        """1903    You can use these recipes like so:1904    \b1905    $ sqlite-utils convert my.db mytable mycolumn \\1906        'r.jsonsplit(value, delimiter=":")'1907    """1908    ).strip()1909    return help1910@cli.command(help=_generate_convert_help())1911@click.argument(1912    "db_path",1913    type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),1914    required=True,1915)1916@click.argument("table", type=str)1917@click.argument("columns", type=str, nargs=-1, required=True)1918@click.argument("code", type=str)1919@click.option(1920    "--import", "imports", type=str, multiple=True, help="Python modules to import"1921)1922@click.option(1923    "--dry-run", is_flag=True, help="Show results of running this against first 10 rows"1924)1925@click.option(1926    "--multi", is_flag=True, help="Populate columns for keys in returned dictionary"1927)1928@click.option("--where", help="Optional where clause")1929@click.option(1930    "-p",1931    "--param",1932    multiple=True,1933    type=(str, str),1934    help="Named :parameters for where clause",1935)1936@click.option("--output", help="Optional separate column to populate with the output")1937@click.option(1938    "--output-type",1939    help="Column type to use for the output column",1940    default="text",1941    type=click.Choice(["integer", "float", "blob", "text"]),1942)1943@click.option("--drop", is_flag=True, help="Drop original column afterwards")1944@click.option("-s", "--silent", is_flag=True, help="Don't show a progress bar")1945def convert(1946    db_path,1947    table,1948    columns,1949    code,1950    imports,1951    dry_run,1952    multi,1953    where,1954    param,1955    output,1956    output_type,1957    drop,1958    silent,1959):1960    sqlite3.enable_callback_tracebacks(True)1961    db = sqlite_utils.Database(db_path)1962    if output is not None and len(columns) > 1:1963        raise click.ClickException("Cannot use --output with more than one column")1964    if multi and len(columns) > 1:1965        raise click.ClickException("Cannot use --multi with more than one column")1966    if drop and not (output or multi):1967        raise click.ClickException("--drop can only be used with --output or --multi")1968    # If single line and no 'return', add the return1969    if "\n" not in code and not code.strip().startswith("return "):1970        code = "return {}".format(code)1971    where_args = dict(param) if param else []1972    # Compile the code into a function body called fn(value)1973    new_code = ["def fn(value):"]1974    for line in code.split("\n"):1975        new_code.append("    {}".format(line))1976    code_o = compile("\n".join(new_code), "<string>", "exec")1977    locals = {}1978    globals = {"r": recipes, "recipes": recipes}1979    for import_ in imports:1980        globals[import_] = __import__(import_)1981    exec(code_o, globals, locals)1982    fn = locals["fn"]1983    if dry_run:1984        # Pull first 20 values for first column and preview them1985        db.conn.create_function("preview_transform", 1, lambda v: fn(v) if v else v)1986        sql = """1987            select1988                [{column}] as value,1989                preview_transform([{column}]) as preview1990            from [{table}]{where} limit 101991        """.format(1992            column=columns[0],1993            table=table,1994            where=" where {}".format(where) if where is not None else "",1995        )1996        for row in db.conn.execute(sql, where_args).fetchall():1997            click.echo(str(row[0]))1998            click.echo(" --- becomes:")1999            click.echo(str(row[1]))2000            click.echo()2001        count = db[table].count_where(2002            where=where,2003            where_args=where_args,2004        )2005        click.echo("Would affect {} row{}".format(count, "" if count == 1 else "s"))2006    else:2007        try:2008            db[table].convert(2009                columns,2010                fn,2011                where=where,2012                where_args=where_args,2013                output=output,2014                output_type=output_type,2015                drop=drop,2016                multi=multi,2017                show_progress=not silent,2018            )2019        except BadMultiValues as e:2020            raise click.ClickException(2021                "When using --multi code must return a Python dictionary - returned: {}".format(2022                    repr(e.values)2023                )2024            )2025def _render_common(title, values):2026    if values is None:2027        return ""2028    lines = [title]2029    for value, count in values:2030        lines.append("    {}: {}".format(count, value))2031    return "\n".join(lines)2032class UnicodeDecodeErrorForPath(Exception):2033    def __init__(self, exception, path):2034        self.exception = exception2035        self.path = path2036FILE_COLUMNS = {2037    "name": lambda p: p.name,2038    "path": lambda p: str(p),2039    "fullpath": lambda p: str(p.resolve()),2040    "sha256": lambda p: hashlib.sha256(p.resolve().read_bytes()).hexdigest(),2041    "md5": lambda p: hashlib.md5(p.resolve().read_bytes()).hexdigest(),2042    "mode": lambda p: p.stat().st_mode,2043    "content": lambda p: p.resolve().read_bytes(),2044    "mtime": lambda p: p.stat().st_mtime,2045    "ctime": lambda p: p.stat().st_ctime,2046    "mtime_int": lambda p: int(p.stat().st_mtime),2047    "ctime_int": lambda p: int(p.stat().st_ctime),2048    "mtime_iso": lambda p: datetime.utcfromtimestamp(p.stat().st_mtime).isoformat(),2049    "ctime_iso": lambda p: datetime.utcfromtimestamp(p.stat().st_ctime).isoformat(),2050    "size": lambda p: p.stat().st_size,2051}2052def output_rows(iterator, headers, nl, arrays, json_cols):2053    # We have to iterate two-at-a-time so we can know if we2054    # should output a trailing comma or if we have reached2055    # the last row.2056    current_iter, next_iter = itertools.tee(iterator, 2)2057    next(next_iter, None)2058    first = True2059    for row, next_row in itertools.zip_longest(current_iter, next_iter):2060        is_last = next_row is None2061        data = row2062        if json_cols:2063            # Any value that is a valid JSON string should be treated as JSON2064            data = [maybe_json(value) for value in data]2065        if not arrays:2066            data = dict(zip(headers, data))2067        line = "{firstchar}{serialized}{maybecomma}{lastchar}".format(2068            firstchar=("[" if first else " ") if not nl else "",2069            serialized=json.dumps(data, default=json_binary),2070            maybecomma="," if (not nl and not is_last) else "",2071            lastchar="]" if (is_last and not nl) else "",2072        )2073        yield line2074        first = False2075def maybe_json(value):2076    if not isinstance(value, str):2077        return value2078    stripped = value.strip()2079    if not (stripped.startswith("{") or stripped.startswith("[")):2080        return value2081    try:2082        return json.loads(stripped)2083    except ValueError:2084        return value2085def json_binary(value):2086    if isinstance(value, bytes):2087        return {"$base64": True, "encoded": base64.b64encode(value).decode("latin-1")}2088    else:2089        raise TypeError2090def _load_extensions(db, load_extension):2091    if load_extension:2092        db.conn.enable_load_extension(True)2093        for ext in load_extension:2094            if ext == "spatialite" and not os.path.exists(ext):2095                ext = find_spatialite()...bot.py
Source:bot.py  
...14            command_prefix=prefix,15            description=DESCRIPTION,16            intents=intents,17        )18        self._load_extensions()19    def _load_extensions(self) -> None:20        cogs_dir = os.listdir("cogs")21        for filename in cogs_dir:22            if filename.endswith(".py"):23                cog = filename[:-3]...Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
