Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support mutating row in --convert without returning it #371

Closed
simonw opened this issue Jan 9, 2022 · 6 comments
Closed

Support mutating row in --convert without returning it #371

simonw opened this issue Jan 9, 2022 · 6 comments
Labels
Milestone

Comments

@simonw
Copy link
Owner

simonw commented Jan 9, 2022

Currently you have to do this:

$ sqlite-utils insert dogs.db dogs dogs.json --convert '
row["is_good"] = 1
return row'

Would be neat if this worked too:

$ sqlite-utils insert dogs.db dogs dogs.json \
  --convert 'row["is_good"] = 1'
@simonw simonw added the cli-tool label Jan 9, 2022
@simonw
Copy link
Owner Author

simonw commented Jan 9, 2022

Might be a case of modifying this line:

docs = (fn(doc) for doc in docs)

To:

docs = (fn(doc) or doc for doc in docs)

@simonw
Copy link
Owner Author

simonw commented Jan 9, 2022

Also need to update relevant docs for that example.

@simonw
Copy link
Owner Author

simonw commented Jan 9, 2022

Tried this test:

    result = CliRunner().invoke(
        cli.cli,
        [
            "insert",
            db_path,
            "rows",
            "-",
            "--convert",
            'row["is_chicken"] = True',
        ],
        input='{"name": "Azi"}',
    )

And got this error:

E + where 1 = <Result SyntaxError('invalid syntax', ('<string>', 2, 30, ' return row["is_chicken"] = True\n'))>.exit_code

The code snippet compilation isn't currently compatible with this.

@simonw
Copy link
Owner Author

simonw commented Jan 9, 2022

Here's the code in question:

# If user defined a convert() function, return that
try:
exec(code, globals, locals)
return locals["convert"]
except (AttributeError, SyntaxError, NameError, KeyError, TypeError):
pass
# Try compiling their code as a function instead
# If single line and no 'return', add the return
if "\n" not in code and not code.strip().startswith("return "):
code = "return {}".format(code)

@simonw
Copy link
Owner Author

simonw commented Jan 9, 2022

This seems to work:

def _compile_code(code, imports, variable="value"):
    locals = {}
    globals = {"r": recipes, "recipes": recipes}
    # If user defined a convert() function, return that
    try:
        exec(code, globals, locals)
        return locals["convert"]
    except (AttributeError, SyntaxError, NameError, KeyError, TypeError):
        pass

    # Try compiling their code as a function instead
    body_variants = [code]
    # If single line and no 'return', try adding the return
    if "\n" not in code and not code.strip().startswith("return "):
        body_variants.insert(0, "return {}".format(code))

    for variant in body_variants:
        new_code = ["def fn({}):".format(variable)]
        for line in variant.split("\n"):
            new_code.append("    {}".format(line))
        try:
            code_o = compile("\n".join(new_code), "<string>", "exec")
            break
        except SyntaxError:
            # Try another variant, e.g. for 'return row["column"] = 1'
            continue

    for import_ in imports:
        globals[import_.split(".")[0]] = __import__(import_)
    exec(code_o, globals, locals)
    return locals["fn"]

@simonw
Copy link
Owner Author

simonw commented Jan 9, 2022

The previous code for highlighting errors in syntax (which was already a bit confused thanks to the added return, see #355 (comment) - isn't compatible with this approach at all. I'm going to ditch it and just show a generic Error: Could not compile code message.

@simonw simonw closed this as completed in 22c8d10 Jan 9, 2022
simonw added a commit that referenced this issue Jan 9, 2022
Should help tests pass for #374, #371
@simonw simonw added this to the 3.21 milestone Jan 10, 2022
simonw added a commit that referenced this issue Jan 11, 2022
simonw added a commit that referenced this issue Jan 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant