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

Functional test framework working with Click, dbtRunner #6387

Merged
merged 13 commits into from
Dec 17, 2022

Conversation

MichelleArk
Copy link
Contributor

@MichelleArk MichelleArk commented Dec 6, 2022

resolves #6096, #6381

Description

  • adds dbtRunner to run dbt CLI programmatically. Accepts profile, project, and manifest (for later), and uses them in combination with CLI args during invoke calls to invoke the CLI.
  • runs tests/functional tests through dbtRunner
    • many integration tests are failing because most commands are not yet implemented, but expected to start passing as we implement commands.
    • did some manual inspection and found tests/functional/dependencies/test_simple_dependency.py::TestSimpleDependencyUnpinned::test_simple_dependency, which only depends on running deps - and succeeds!
  • runs test/integration tests last so that we can start surfacing failing functional tests in CI logs.
  • sets a couple param defaults to callables so they are set at runtime as opposed to param.py import. This is needed for correct fixture setup which set the project/profile dirs dynamically.

Checklist

@cla-bot cla-bot bot added the cla:yes label Dec 6, 2022
@github-actions
Copy link
Contributor

github-actions bot commented Dec 6, 2022

Thank you for your pull request! We could not find a changelog entry for this change. For details on how to document a change, see the contributing guide.

@MichelleArk MichelleArk force-pushed the click-functional-tests branch from b608756 to d37d023 Compare December 7, 2022 21:51
core/dbt/cli/context.py Outdated Show resolved Hide resolved
core/dbt/tests/util.py Outdated Show resolved Hide resolved
@MichelleArk MichelleArk force-pushed the click-functional-tests branch from 3efaf94 to a58b1fe Compare December 8, 2022 20:55
core/dbt/cli/context.py Outdated Show resolved Hide resolved
core/dbt/cli/context.py Outdated Show resolved Hide resolved
core/dbt/cli/context.py Outdated Show resolved Hide resolved
core/dbt/cli/context.py Outdated Show resolved Hide resolved
core/dbt/cli/context.py Outdated Show resolved Hide resolved
@MichelleArk MichelleArk force-pushed the click-functional-tests branch 2 times, most recently from 0d49f0c to e298039 Compare December 12, 2022 22:14
ctx.obj["project"] = project

# Context for downstream commands
ctx.obj["flags"] = flags
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not saying we need to change it but since we actually construct flags, profile, project within the command, is ctx.obj still the best place to put it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, definitely worth questioning! An alternative could be to inject profile, project, manifest, etc into the command method's args, similar to how click.pass_context works. This way we'd be a little less coupled to click, although we'd probably still need to use the click context to store any project/profile overrides passed to dbtRunner. There would be some naming conflicts to resolve (ex: params from options named --profile and a profile override).

I think it's worth keeping in mind alternatives to using ctx.obj for passing user-configured objects to commands in mind as we go as the current approach doesn't preclude us from changing this up in the future.

core/dbt/cli/main.py Outdated Show resolved Hide resolved


# Programmatic invocation
class dbtRunner:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this first rough version of thin wrapper, for end users who want to programmatically invoke dbt-core via Python API? Thinking:

    # initialize longer-lived dbtRunner
    # by implicitly loading project/profile/etc based on config + cwd
    dbt = dbtRunner()
    
    # submit commands to dbtRunner
    # each command returns
    #   results: List[Result]
    #   success: bool
    results, success = dbt.run(...)
    results, success = dbt.test(...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, exactly!

# initialize longer-lived dbtRunner
# by implicitly loading project/profile/etc based on config + cwd
dbt = dbtRunner()

^ We could load profile/project/etc from defaults at during dbtRunner instantiation but depending on the args in subsequent commands, they may require reloading. Something to consider for a follow-up though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    results, success = dbt.run(...)
    results, success = dbt.test(...)

this part is tbd I think? current way would be pass in command through args, the main advantage is some CLI args need to be specified before the actual command. So exposing things this way provide best flexibility at the moment.

args = ['run', '--project-dir', 'something']
res, success = dbt.invoke(args)

Copy link
Contributor

@jtcohen6 jtcohen6 Dec 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

current way would be pass in command through args

We'll definitely want to support this generic pattern ("given an arbitrary list of CLI arg strings, invoke"). I could also see us wanting to support a more structured approach, where users can build up a dictionary of attributes and pass it into a named command. All of which is to say, we should design this programmatic interface for real, before we commit to it as a public-facing API — and that means sitting down, writing some user stories, talking with community members. All good stuff to do next year :)

the main advantage is some CLI args need to be specified before the actual command

Lukewarm take: This is pretty confusing for end users! We should really seek to support flags in any order (and resolve any resulting ambiguities by actually renaming flags). I know we implemented the new CLI modeling in accordance with the old CLI model — parity is a good goal to shoot for with any refactor — but it would be great to just support both of these, seamlessly and equivalently:

$ dbt --no-use-color run --select one_model
$ dbt run --select one_model --no-use-color

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that wasn't part of the original plan, but I guess we can see about supporting arbitrary flag ordering-- It's not something we should take on in this PR though. @jtcohen6, can you write up a ticket that we can put into the estimation process.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iknox-fa Totally - opened here: #6497

@MichelleArk MichelleArk force-pushed the click-functional-tests branch from 2995132 to 5a1c687 Compare December 13, 2022 22:09
@MichelleArk MichelleArk changed the title first pass: dbt.cli.main.handle_and_check Functional test framework working with Click, dbtRunner Dec 13, 2022
@MichelleArk MichelleArk marked this pull request as ready for review December 14, 2022 14:08
@MichelleArk MichelleArk requested a review from a team as a code owner December 14, 2022 14:08
@MichelleArk MichelleArk requested review from gshank, aranke and ChenyuLInx and removed request for gshank and aranke December 14, 2022 14:08
@MichelleArk MichelleArk assigned iknox-fa and unassigned iknox-fa Dec 14, 2022
@MichelleArk MichelleArk requested a review from iknox-fa December 14, 2022 15:18
# TODO: Decouple target from profile, and remove the need for profile here:
# https://github.com/dbt-labs/dbt-core/issues/6257
if not ctx.obj.get("profile"):
raise DbtProjectError("profile required for project")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a more appropriate exception to raise here? I'm not too concerned either way because we should be able to get away from requiring a profile at which point the exception won't be necessary here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also check for requires.profile is added to the current command or not?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that may be overly-rigid. Technically this method only requires profile to exist in ctx.obj, and shouldn't need to be concerned with how it gets there. Adding that check would make it unnecessarily difficult to change the 'how'. Also not sure it's worth the effort given we're looking to decouple profile from project creation.

Copy link
Contributor

@iknox-fa iknox-fa Dec 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a more appropriate exception to raise here?

This seems appropriate to me. I do know that exceptions are getting some closer attention from @emmyoop and others soon so I wouldn't stress it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also a DbtProfileError Exception that is also a subclass of `DbtConfigError.

@MichelleArk MichelleArk requested a review from stu-k December 14, 2022 21:37
Copy link
Contributor

@stu-k stu-k left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. There is one test.py file that seems to be empty that you can probably remove.

@MichelleArk MichelleArk force-pushed the click-functional-tests branch from 28406d9 to c2a8ef6 Compare December 15, 2022 23:51
Copy link
Contributor

@ChenyuLInx ChenyuLInx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, also tried to extend this in another PR, works great!

{envpython} -m pytest --cov=core {posargs} tests/functional
{envpython} -m pytest --cov=core {posargs} tests/adapter
{envpython} -m pytest --cov=core -m profile_postgres {posargs} test/integration
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing important, but I'm curious why the test run-order changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the test/integration tests will continue to fail on this branch, I moved them to run last so that errors from tests/functional would be visible in CI logs

@@ -73,7 +73,8 @@ def run_dbt(args: List[str] = None, expect_pass=True):
args = ["run"]

print("\n\nInvoking dbt with {}".format(args))
res, success = handle_and_check(args)
dbt = dbtRunner()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love how simple this change ended up being. It's a sign that we made the right abstractions when we implemented the new functional testing framework.

Copy link
Contributor

@iknox-fa iknox-fa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great @MichelleArk!
Ship it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants