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
Create shared environment across nested import/validation loops via a metadata object #266
Conversation
This patch introduces a metadata object that is propagated through every level of a recursive import/validation process. It is used to carry options and other data that need to be available throughout the entire process, not just during the outermost loop.
I like the idea here, having the environment accessible during the loop helps to keep any options inspectionable during the recursion. Do we need the pass-by-copy though? Couldn't we devise a structure similar to namedtuple that would be immutable during the loop? Also, the |
I like this, but: Simple types will still need to receive I agree with @lkraider about Since this is a (mildly) breaking change, we'd need to bump the minor version at least. |
Sorry, it is not clear to me what problem this solves. The reason the arguments from the outer most loop are locked in is to keep the arguments consistent throughout the entire process. If you want a different result, export with different arguments. Could you describe the problem this change solves? |
OK. I am thinking on this here. Are you saying the value for |
Yes. And neither does |
ohhhhh well THAT is a bug that should be fixed. I don't think we need to pass around an environment variable as much as we need subsequent calls to use the same keyword arguments. I am not sure what the cause is yet, but it's supposed to copy the initial arguments the whole way down. |
The reason I prefer a single environment object is that, once it has been implemented, it's zero maintenance going forward. The same cannot be said about repeating this huge argument list in 100 methods around the codebase, particularly since authors of custom types also need to keep their code in sync with the full range of available arguments. Reliance on individual keyword args is probably what has led to this kind of conditional code. |
Hmm. OK. I better understand what you're trying to do now. I see the point about every type having to support the same keyword arguments now too. What we'd get with this approach is a dict that has all the values we care about, but rogue fields would be ignored by types that don't know how to use particular values. This is the primary strength as I see it. I think I am on board with this but wanna think on it a little longer. Thanks for explaining that as I worked my way through what you're thinking about. |
srsly blowing my mind that the arguments weren't preserved... what's even we'll get this worked out properly. On Tue, Oct 20, 2015 at 5:00 PM, Kirk Strauser notifications@github.com
jms dnns |
Hey, we were all surprised by that one. 😄 This is off-topic for this particular PR, but I'll mention it anyway: even if we merged this today we'd still need to reimplement #318 on top of it to get strict working. I'd be so happy to throw away all the special-case code and start over with a clean foundation, though. |
I've been away from schematics for a while, so my memory on things is if we know what the ideal solution to something is, let's always do that. we'll probably do this one, and then i guess that will require a new i'll think on this one and hopefully merge it in really soon. On Tue, Oct 20, 2015 at 5:05 PM, Kirk Strauser notifications@github.com
jms dnns |
I haven't had the opportunity to be as active lately as in the past, but I haven't forgotten about it. Also, #324 is still open. One day it may be appropriate to have a new major version with a new API. Or not. But we can discuss it to see what makes the most sense! |
I've also been inactive for most of this year due to vision problems that kept me away from coding. In the past few weeks I've been looking at Schematics again, and it's an interesting coincidence that @jmsdnns is now back as well. Another reason this PR was left pending is that it's a kind of breaking change. And if we're going to break things, then it would make the most sense to try to identify as many legacy pain points as possible and target a new (major?) release that would bundle all the backward-incompatible changes together. Then, the API could again remain constant for the foreseeable future. Also, something to figure out is what to do with the arguments (such as The way I envisioned this, individual arguments (existing and future) should remain available in methods that are typically directly called by application code using Schematics. For example, |
I think it's time to move on this, mostly because the Undefined feature needs this, but also because it's been nearly a year and there's no point in waiting any longer. I'm currently rewriting the PR, and I'd like some input on what to do with Based on prior discussion, the The other usage ("arbitrary user data") could otherwise continue to be called "context", but I think we'd like all conversion functions to accept such arbitrary data, including That said, once instance.import_data({'name': 'bintoro'}, partial=True, strict=True) instance.import_data({'name': 'bintoro'}, env=ConfigObject(partial=True, strict=True)) These two statements are equivalent. In the former, If we forgo this convenience for The other option is to eliminate "context" throughout the library and replace it with something like "private", "app", "user", ... (Basically, My current thinking is that we could just drop
EDIT: And just to be clear, instance.import_data({...}, partial=True, strict=True, env=ConfigObject(context={...})) Also, the code that wants to use the context data is going to access it via EDIT 2: I'll probably add support for supplying instance.import_data({...}, partial=True, strict=True, env={'context': {...}}) |
Closing in favor of #359. |
Summary
This patch introduces a metadata object that is propagated through every level of a recursive import/validation process. It is used to carry options and other data that need to be available throughout the entire process, not just during the outermost loop.
Background
One thing currently blocking the
Undefined
feature is the fact that import/validation parameters (importantly,partial=True
) only affect the first level of processing. If you have nested models, your settings no longer apply.Apparently someone has noticed this before and created this construct in
import_loop
to pass the deserialization mapping down to nested models:And there was a similar
try—except
inside thefield_converter
in question.The patch
EDIT: Changed param name from
meta
toenv
.Instead of adding yet another parameter here, I have future-proofed this by turning the third parameter into a multipurpose object representing the current environment. Import converters and validators can now propagate options and whatever data they please in the
env
parameter. One practical application might be bookkeeping during validation; currently it's possible to have aModelType
structure that gets into an infinite loop.The above code now looks like this:
The way this works is any function (including the initial caller) could decide to set up the
Environment
object. Once it enters the processing chain, its contents override any overlapping parameters passed as keyword args (EDIT: not by necessity, of course, but becauseimport_loop
chooses to treat it that way).Right now, the setup is the responsibility of the outermost
import_loop
. It takes thestrict
andpartial
arguments it receives and locks them in for the rest of the import recursion.As an aside, if all options were collated into a
env
object from the start, the parameter signature ofimport_loop
,convert
, and friends could look like this:Although incorporating many options into a single object may be considered a bit opaque, it's clearly preferable to always passing a dozen values to every method and their aunts:
That's five calls for each level of recursion.
BREAKING CHANGE WARNING
I've gotten rid of the
try–except
constructs that otherwise would have been all over the place.Considering that the
env
container is primarily needed in a recursive process, the method signature dilemma has been solved by requiring compound types (MultiType
descendants) to accept theenv
argument in theirto_native
andvalidate_
methods. Simple types are called withoutenv
.Since custom compound types need a (trivial) change as a result, this would be a great opportunity also to get rid of the
context
parameter that appears in a ton of places. It would be better placed insideenv
as well.Other changes incorporated here
Model.__init__
now accepts thepartial
parameter too (default=True
because it was the de facto default already, specified in the mainconvert
func).ListType.validate_items()
was added to the list of validators twice: once explicitly and once via collation byTypeMeta
.