-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Abstract manifest generation from tasks #6565
Merged
Merged
Changes from 6 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
a0a4d5b
Abstract manifest generation from tasks
stu-k 8615b39
Add generated CLI API docs
FishtownBuildBot c4ac379
decorators, writing, remove env var
stu-k 2237873
Merge branch 'feature/click-cli' into CT-1582/abstract-task-manifest-gen
stu-k 0f1ecad
Remove parse from legacy main.py
stu-k e5fea9a
rebuild docs
stu-k 592033e
take manifest in task init, write in decorator
stu-k b673061
Add generated CLI API docs
FishtownBuildBot d42de06
remove unused kwargs to write_manifest
stu-k 0945988
Refactor RunOperation unit tests
stu-k c675dd9
Merge branch 'feature/click-cli' into CT-1582/abstract-task-manifest-gen
stu-k 4a62ada
Manifest decorator doesn't overwrite
stu-k 22eb4a7
Regen docs, fix error
stu-k a7e778b
Use manifest decorator, pass into new tasks
stu-k b6d7ea2
Add generated CLI API docs
FishtownBuildBot fcf49a9
Fix manifest typos
stu-k 28efb2a
Add generated CLI API docs
FishtownBuildBot c1a1fe8
Add compile_manifest back to BuildTask
stu-k File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
kind: Under the Hood | ||
body: Abstract manifest generation | ||
time: 2023-01-10T11:57:25.193965-06:00 | ||
custom: | ||
Author: stu-k | ||
Issue: "6357" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
from dbt.adapters.factory import adapter_management | ||
from dbt.adapters.factory import adapter_management, register_adapter | ||
from dbt.cli.flags import Flags | ||
from dbt.config import RuntimeConfig | ||
from dbt.config.runtime import load_project, load_profile | ||
from dbt.events.functions import setup_event_logger | ||
from dbt.exceptions import DbtProjectError | ||
from dbt.parser.manifest import ManifestLoader | ||
from dbt.profiler import profiler | ||
from dbt.tracking import initialize_from_flags, track_run | ||
|
||
|
@@ -85,3 +87,64 @@ def wrapper(*args, **kwargs): | |
return func(*args, **kwargs) | ||
|
||
return update_wrapper(wrapper, func) | ||
|
||
|
||
def runtime_config(func): | ||
"""A decorator used by click command functions for generating a runtime | ||
config given a profile and project. | ||
""" | ||
|
||
def wrapper(*args, **kwargs): | ||
ctx = args[0] | ||
assert isinstance(ctx, Context) | ||
|
||
req_strs = ["profile", "project"] | ||
reqs = [ctx.obj.get(req_str) for req_str in req_strs] | ||
|
||
if None in reqs: | ||
raise DbtProjectError("profile and project required for runtime_config") | ||
|
||
ctx.obj["runtime_config"] = RuntimeConfig.from_parts( | ||
ctx.obj["project"], | ||
ctx.obj["profile"], | ||
ctx.obj["flags"], | ||
) | ||
|
||
return func(*args, **kwargs) | ||
|
||
return update_wrapper(wrapper, func) | ||
|
||
|
||
def manifest(*args0, write_perf_info=False): | ||
stu-k marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"""A decorator used by click command functions for generating a manifest given a profile, project, | ||
and runtime config. This also registers the adaper from the runtime config. | ||
""" | ||
|
||
def outer_wrapper(func): | ||
def wrapper(*args, **kwargs): | ||
ctx = args[0] | ||
assert isinstance(ctx, Context) | ||
|
||
req_strs = ["profile", "project", "runtime_config"] | ||
reqs = [ctx.obj.get(dep) for dep in req_strs] | ||
|
||
if None in reqs: | ||
raise DbtProjectError("profile, project, and runtime_config required for manifest") | ||
|
||
runtime_config = ctx.obj["runtime_config"] | ||
register_adapter(runtime_config) | ||
manifest = ManifestLoader.get_full_manifest( | ||
runtime_config, write_perf_info=write_perf_info | ||
) | ||
|
||
ctx.obj["manifest"] = manifest | ||
|
||
return func(*args, **kwargs) | ||
|
||
return update_wrapper(wrapper, func) | ||
|
||
# if there are no args, the decorator was used without params @decorator | ||
# otherwise, the decorator was called with params @decorator(arg) | ||
if len(args0) == 0: | ||
return outer_wrapper | ||
return outer_wrapper(args0[0]) | ||
Comment on lines
+152
to
+156
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a better more pythonic way to have a decorator accept args? |
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
# Sphinx build info version 1 | ||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. | ||
config: e27d6c1c419f2f0af393858cdf674109 | ||
config: 0d25ef12a43286020bcd8b805064f01c | ||
tags: 645f666f9bcd5a90fca523b33c5a78b7 |
134 changes: 134 additions & 0 deletions
134
core/dbt/docs/build/html/_static/_sphinx_javascript_frameworks_compat.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
/* | ||
* _sphinx_javascript_frameworks_compat.js | ||
* ~~~~~~~~~~ | ||
* | ||
* Compatability shim for jQuery and underscores.js. | ||
* | ||
* WILL BE REMOVED IN Sphinx 6.0 | ||
* xref RemovedInSphinx60Warning | ||
* | ||
*/ | ||
|
||
/** | ||
* select a different prefix for underscore | ||
*/ | ||
$u = _.noConflict(); | ||
|
||
|
||
/** | ||
* small helper function to urldecode strings | ||
* | ||
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL | ||
*/ | ||
jQuery.urldecode = function(x) { | ||
if (!x) { | ||
return x | ||
} | ||
return decodeURIComponent(x.replace(/\+/g, ' ')); | ||
}; | ||
|
||
/** | ||
* small helper function to urlencode strings | ||
*/ | ||
jQuery.urlencode = encodeURIComponent; | ||
|
||
/** | ||
* This function returns the parsed url parameters of the | ||
* current request. Multiple values per key are supported, | ||
* it will always return arrays of strings for the value parts. | ||
*/ | ||
jQuery.getQueryParameters = function(s) { | ||
if (typeof s === 'undefined') | ||
s = document.location.search; | ||
var parts = s.substr(s.indexOf('?') + 1).split('&'); | ||
var result = {}; | ||
for (var i = 0; i < parts.length; i++) { | ||
var tmp = parts[i].split('=', 2); | ||
var key = jQuery.urldecode(tmp[0]); | ||
var value = jQuery.urldecode(tmp[1]); | ||
if (key in result) | ||
result[key].push(value); | ||
else | ||
result[key] = [value]; | ||
} | ||
return result; | ||
}; | ||
|
||
/** | ||
* highlight a given string on a jquery object by wrapping it in | ||
* span elements with the given class name. | ||
*/ | ||
jQuery.fn.highlightText = function(text, className) { | ||
function highlight(node, addItems) { | ||
if (node.nodeType === 3) { | ||
var val = node.nodeValue; | ||
var pos = val.toLowerCase().indexOf(text); | ||
if (pos >= 0 && | ||
!jQuery(node.parentNode).hasClass(className) && | ||
!jQuery(node.parentNode).hasClass("nohighlight")) { | ||
var span; | ||
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); | ||
if (isInSVG) { | ||
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); | ||
} else { | ||
span = document.createElement("span"); | ||
span.className = className; | ||
} | ||
span.appendChild(document.createTextNode(val.substr(pos, text.length))); | ||
node.parentNode.insertBefore(span, node.parentNode.insertBefore( | ||
document.createTextNode(val.substr(pos + text.length)), | ||
node.nextSibling)); | ||
node.nodeValue = val.substr(0, pos); | ||
if (isInSVG) { | ||
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); | ||
var bbox = node.parentElement.getBBox(); | ||
rect.x.baseVal.value = bbox.x; | ||
rect.y.baseVal.value = bbox.y; | ||
rect.width.baseVal.value = bbox.width; | ||
rect.height.baseVal.value = bbox.height; | ||
rect.setAttribute('class', className); | ||
addItems.push({ | ||
"parent": node.parentNode, | ||
"target": rect}); | ||
} | ||
} | ||
} | ||
else if (!jQuery(node).is("button, select, textarea")) { | ||
jQuery.each(node.childNodes, function() { | ||
highlight(this, addItems); | ||
}); | ||
} | ||
} | ||
var addItems = []; | ||
var result = this.each(function() { | ||
highlight(this, addItems); | ||
}); | ||
for (var i = 0; i < addItems.length; ++i) { | ||
jQuery(addItems[i].parent).before(addItems[i].target); | ||
} | ||
return result; | ||
}; | ||
|
||
/* | ||
* backward compatibility for jQuery.browser | ||
* This will be supported until firefox bug is fixed. | ||
*/ | ||
if (!jQuery.browser) { | ||
jQuery.uaMatch = function(ua) { | ||
ua = ua.toLowerCase(); | ||
|
||
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || | ||
/(webkit)[ \/]([\w.]+)/.exec(ua) || | ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || | ||
/(msie) ([\w.]+)/.exec(ua) || | ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || | ||
[]; | ||
|
||
return { | ||
browser: match[ 1 ] || "", | ||
version: match[ 2 ] || "0" | ||
}; | ||
}; | ||
jQuery.browser = {}; | ||
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changes in this PR mean that:
dbt parse
will always write the manifest (= overwritemanifest.json
). IMO that's a good change! I'd opened an issue for it recently: [CT-1759]dbt parse
should (over)writemanifest.json
by default #6534. So we can also remove--write-manifest
as a parameter for this command, and inparams.py
.dbt parse --compile
, which allows us to output detailed performance timing that includes the DAG construction step (= resolving ephemeral model references + linking nodes/edges).For the second point: I'd be happy with kicking that out of scope for this PR, and opening a tech debt ticket to track it. There's a bigger idea here: "Abstract manifest compilation / graph generation from tasks." Idea being, move
compile_manifest()
out from task initialization, into either a conditional step within@requires.manifest
, or as its own@requires.graph
step. Then, tasks would accept the graph as an argument.Considerations there:
RuntimeConfig
, as an inputGraph
build
task produces + uses a differentgraph
from all other tasks, with extra test edgesgraph
onctx.obj
, and passed it into each task explicitly, we would unlock the ability for programmatic invocations to cache + reuse thegraph
between invocations. That could make a difference for performance in very large projects. (With the important caveat that the external application would be responsible for distinguishing between standard andbuild
-specific graphs.)Finally, a separate proposal, out of scope for this PR but highly relevant: The
parse
task should return theManifest
here, instead ofNone
(#6547). That could be as simple as changing this last line to:And then:
There's a bit more refinement we should do on that proposal first, to make sure it's an idea we're happy with.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the context. I had spoken with Gerda about the function of the existing
ParseTask
class, and she had said I could take out the tracking. Adding it back in shouldn't be difficult, and could be done conditionally.For your proposal of returning the manifest from the task directly, I think that's something we need to discuss on the exec team. I believe we should have a standard output for each of these functions, perhaps a dict with a
result
ormessage
or whatever key should contain result, in case we want to add meta information to that result object later.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine with me too. Gerda consolidated all the events in this task, anyway, to just
ParseCmdOut
(in themain
branch). We'll just want to remove that event now that it's no longer being called anywhere. For simplicity, let's track that in a separate issue, rather than try to do branch merging shenanigans here.The point I was making above was around,
dbt parse --compile
would also conditionally include the step around manifest compilation / graph generation, which can be very slow (as we've seen in recent reports/issues from folks with large projects). Let's track that in the new issue, "Abstract manifest compilation / graph generation from tasks."Agreed! Let's keep discussing in the separate linked issue. The biggest considerations are:
results
relatively consistent for all commands (although different tasks already return differently typed result objects)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might want to call these changes out here:
https://docs.getdbt.com/guides/migration/versions/upgrading-to-v1.5
Did the proposed "Abstract manifest compilation / graph generation from tasks." issue ever get created?
In the meantime, I'm going to delete both of these from
params.py
as part of #6546 since they don't appear to do anything:dbt-core/core/dbt/cli/params.py
Lines 44 to 49 in 3f76f82
dbt-core/core/dbt/cli/params.py
Lines 491 to 496 in 3f76f82