U `CU@s6ddlmZmZmZmZmZmZddlZddlZddl m Z ddl Te e ZdZGdddeZGdddeZGd d d eZGd d d eZGd ddeZGdddeZGdddeZGdddeZGdddeZGdddeZGdddeZGdddeZGdddeZGdd d eZdS)!)unicode_literalsprint_functionabsolute_importdivision generators nested_scopesN)xrange)*c@s8eZdZdZddZddZddZdd Zd d Zd S) JSONPathz The base class for JSONPath abstract syntax; those methods stubbed here are the interface to supported JSONPath semantics. cCs tdS)a All `JSONPath` types support `find()`, which returns an iterable of `DatumInContext`s. They keep track of the path followed to the current location, so if the calling code has some opinion about that, it can be passed in here as a starting point. NNotImplementedErrorselfdatar8/tmp/pip-unpacked-wheel-e4vcopd5/jsonpath_ng/jsonpath.pyfindsz JSONPath.findcCs tdS)z~ Returns `data` with the specified path replaced by `val`. Only updates if the specified path exists. Nr rrvalrrrupdateszJSONPath.updatecCs tdS)aM Returns `data` with the specified path filtering nodes according the filter evaluation result returned by the filter function. Arguments: fn (function): unary function that accepts one argument and returns bool. data (dict|list|tuple): JSON object to filter. Nr rfnrrrrfilter&s zJSONPath.filtercCsBt|tst|tr|St|tr&|St|tr4|St||SdS)zP Equivalent to Child(self, next) but with some canonicalization N) isinstanceThisRootChild)rchildrrrr3s  zJSONPath.childcCs"t|tr|St|tddSdSNpathcontext)rDatumInContextr)rvaluerrr make_datum@s zJSONPath.make_datumN) __name__ __module__ __qualname____doc__rrrrr$rrrrr s   r c@sVeZdZdZeddZdddZddZed d Z ed d Z d dZ ddZ dS)r"a Represents a datum along a path from a context. Essentially a zipper but with a structure represented by JsonPath, and where the context is more of a parent pointer than a proper representation of the context. For quick-and-dirty work, this proxies any non-special attributes to the underlying datum, but the actual datum can (and usually should) be retrieved via the `value` attribute. To place `datum` within another, use `datum.in_context(context=..., path=...)` which extends the path. If the datum already has a context, it places the entire context within that passed in, so an object can be built from the inside out. cCst||r|S||SdSN)r)clsrrrrwrapXs zDatumInContext.wrapNcCs.||_|pt|_|dkrdnt||_dSr))r#rr r"r+r!)rr#r r!rrr__init___s zDatumInContext.__init__cCs@t|}|jr,t|j|j|j||ddSt|j||dSdS)Nrr#r r!)r"r+r!r#r in_contextrr!r rrrr.ds zDatumInContext.in_contextcCs |jdkr|jS|jj|jSr))r!r full_pathrrrrrr0lszDatumInContext.full_pathc CsTztt|jt}Wn tttfk r6|j}YnX|jrL|jj |S|SdS)zI Looks like a path, but with ids stuck in when available N) Fieldsstrr# auto_id_field TypeErrorAttributeErrorKeyErrorr r! id_pseudopathr)rZ pseudopathrrrr8ps zDatumInContext.id_pseudopathcCsd|jj|j|j|jfS)Nz!%s(value=%r, path=%r, context=%r)) __class__r%r#r r!r1rrr__repr__szDatumInContext.__repr__cCs.t|to,|j|jko,|j|jko,|j|jkSr))rr"r#r r!rotherrrr__eq__szDatumInContext.__eq__)NN) r%r&r'r( classmethodr+r,r.propertyr0r8r:r=rrrrr"Gs    r"c@sVeZdZdZdddZeddZeddZed d Zd d Z d dZ ddZ dS)AutoIdForDatuma This behaves like a DatumInContext, but the value is always the path leading up to it, not including the "id", and with any "id" fields along the way replacing the prior segment of the path For example, it will make "foo.bar.id" return a datum that behaves like DatumInContext(value="foo.bar", path="foo.bar.id"). This is disabled by default; it can be turned on by settings the `auto_id_field` global to a value other than `None`. NcCs||_|p t|_dS)aW Invariant is that datum.path is the path from context to datum. The auto id will either be the id in the datum (if present) or the id of the context followed by the path to the datum. The path to this datum is always the path to the context, the path to the datum, and then the auto id field. N)datumr4id_field)rrArBrrrr,s zAutoIdForDatum.__init__cCs t|jjSr))r3rAr8r1rrrr#szAutoIdForDatum.valuecCs|jSr))rBr1rrrr szAutoIdForDatum.pathcCs|jSr)rAr1rrrr!szAutoIdForDatum.contextcCsd|jj|jfS)Nz%s(%r))r9r%rAr1rrrr:szAutoIdForDatum.__repr__cCst|jj||dS)Nr!r )r@rAr.r/rrrr.szAutoIdForDatum.in_contextcCs"t|to |j|jko |j|jkSr))rr@rArBr;rrrr=szAutoIdForDatum.__eq__)N) r%r&r'r(r,r?r#r r!r:r.r=rrrrr@s    r@c@s@eZdZdZddZddZddZdd Zd d Zd d Z dS)rz The JSONPath referring to the "root" object. Concrete syntax is '$'. The root is the topmost datum without any context attached. cCsLt|tst|tddgS|jdkr:t|jdtdgSt|jSdS)NrrD)rr"rr!r#rr rrrrs   z Root.findcCs|Sr)rrrrrrsz Root.updatecCs||r |SdSr)rrrrrrsz Root.filtercCsdS)N$rr1rrr__str__sz Root.__str__cCsdS)NzRoot()rr1rrrr:sz Root.__repr__cCs t|tSr))rrr;rrrr=sz Root.__eq__N r%r&r'r(rrrrFr:r=rrrrrs rc@s@eZdZdZddZddZddZdd Zd d Zd d Z dS)rzN The JSONPath referring to the current datum. Concrete syntax is '@'. cCs t|gSr))r"r+rrArrrrsz This.findcCs|Sr)rrrrrrsz This.updatecCs||r |SdSr)rrrrrrsz This.filtercCsdS)Nz`this`rr1rrrrFsz This.__str__cCsdS)NzThis()rr1rrrr:sz This.__repr__cCs t|tSr))rrr;rrrr=sz This.__eq__NrGrrrrrsrc@sHeZdZdZddZddZddZdd Zd d Zd d Z ddZ dS)rzi JSONPath that first matches the left, then the right. Concrete syntax is '.' cCs||_||_dSr)leftrightrrJrKrrrr,szChild.__init__csfddj|DS)z Extra special case: auto ids do not have children, so cut it off right now rather than auto id the auto id cs,g|]$}t|tsj|D]}|qqSr)rr@rKr).0subdatasubmatchr1rr s  zChild.find..rJrrHrr1rrs  z Child.findcCs&|j|D]}|j|j|q |Sr))rJrrKrr#rrrrArrrrsz Child.updatecCs&|j|D]}|j||jq |Sr))rJrrKrr#rrrrArrrrsz Child.filtercCs"t|to |j|jko |j|jkSr))rrrJrKr;rrrr= sz Child.__eq__cCsd|j|jfS)Nz%s.%srIr1rrrrFsz Child.__str__cCsd|jj|j|jfS)Nz %s(%r, %r))r9r%rJrKr1rrrr:szChild.__repr__N) r%r&r'r(r,rrrr=rFr:rrrrrs rc@s0eZdZdZddZddZddZdd Zd S) Parentz JSONPath that matches the parent node of the current match. Will crash if no such parent exists. Available via named operator `parent`. cCst|}|jgSr))r"r+r!rHrrrrs z Parent.findcCs t|tSr))rrTr;rrrr="sz Parent.__eq__cCsdS)Nz`parent`rr1rrrrF%szParent.__str__cCsdS)NzParent()rr1rrrr:(szParent.__repr__N)r%r&r'r(rr=rFr:rrrrrTs rTc@s@eZdZdZddZddZddZdd Zd d Zd d Z dS)Wherez JSONPath that first matches the left, and then filters for only those nodes that have a match on the right. WARNING: Subject to change. May want to have "contains" or some other better word for it. cCs||_||_dSr)rIrLrrrr,6szWhere.__init__csfddj|DS)Ncsg|]}j|r|qSr)rKr)rMrNr1rrrP;s zWhere.find..rQr rr1rr:sz Where.findcCs"||D]}|j||q |Sr)rr rrRrrrr=sz Where.updatecCs$||D]}|j||jq |Sr))rr rr#rSrrrrBsz Where.filtercCsd|j|jfS)Nz %s where %srIr1rrrrFGsz Where.__str__cCs"t|to |j|jko |j|jkSr))rrUrJrKr;rrrr=Jsz Where.__eq__N) r%r&r'r(r,rrrrFr=rrrrrU,s rUc@sHeZdZdZddZddZddZdd Zd d Zd d Z ddZ dS) Descendantsz{ JSONPath that matches first the left expression then any descendant of it which matches the right expression. cCs||_||_dSr)rIrLrrrr,SszDescendants.__init__cs<j|}t|ts|g}fddfdd|DS)Ncsrj}tjtr:fddtdtjD}n,tjtrbfddjD}ng}|t|S)Ncs2g|]*}tj|t|dD]}|q$qSrDr"r#Index)rMirOrAmatch_recursivelyrrrPhsz?Descendants.find..match_recursively..rcs2g|]*}tj|t|dD]}|q$qSrX)r"r#r2)rMfieldrOr\rrrPms) rKrrr#listrangelendictkeys)rAZ right_matchesZrecursive_matchesr]rrCrr]cs     z+Descendants.find..match_recursivelycsg|]}|D]}|qqSrr)rMZ left_matchrO)r]rrrPws z$Descendants.find..)rJrrr_)rrA left_matchesrrdrrWs   zDescendants.findcCsdSNFrr1rrr is_singular{szDescendants.is_singularcsDj|}t|ts|g}fdd|D]}|jq0|S)Ncszt|tst|tsdSj|t|trRtdt|D]}||q>n$t|trv|D]}||qddSNr)rr_rbrKrr`rarcrr[r^rupdate_recursivelyrrrrks   z.Descendants.update..update_recursivelyrJrrr_r#)rrrrerOrrjrr~s   zDescendants.updatecsDj|}t|ts|g}fdd|D]}|jq0|S)Ncszt|tst|tsdSj|t|trRtdt|D]}||q>n$t|trv|D]}||qddSrh)rr_rbrKrr`rarcrifilter_recursivelyrrrrrns   z.Descendants.filter..filter_recursivelyrl)rrrrerOrrmrrs   zDescendants.filtercCsd|j|jfS)Nz%s..%srIr1rrrrFszDescendants.__str__cCs"t|to |j|jko |j|jkSr))rrWrJrKr;rrrr=szDescendants.__eq__N) r%r&r'r(r,rrgrrrFr=rrrrrWMs$rWc@s(eZdZdZddZddZddZdS) Unionas JSONPath that returns the union of the results of each match. This is pretty shoddily implemented for now. The nicest semantics in case of mismatched bits (list vs atomic) is to put them all in a list, but I haven't done that yet. WARNING: Any appearance of this being the _concatenation_ is coincidence. It may even be a bug! (or laziness) cCs||_||_dSr)rIrLrrrr,szUnion.__init__cCsdSrfrr1rrrrgszUnion.is_singularcCs|j||j|Sr))rJrrKr rrrrsz Union.findNr%r&r'r(r,rgrrrrrros roc@s(eZdZdZddZddZddZdS) IntersectaL JSONPath for bits that match *both* patterns. This can be accomplished a couple of ways. The most efficient is to actually build the intersected AST as in building a state machine for matching the intersection of regular languages. The next idea is to build a filtered data and match against that. cCs||_||_dSr)rIrLrrrr,szIntersect.__init__cCsdSrfrr1rrrrgszIntersect.is_singularcCs tdSr)r r rrrrszIntersect.findNrprrrrrqs rqc@sXeZdZdZddZddZddZdd Zd d Zd d Z ddZ ddZ ddZ dS)r2z JSONPath referring to some field of the current object. Concrete syntax ix comma-separated field names. WARNING: If '*' is any of the field names, then they will all be returned. cGs ||_dSr))fields)rrrrrrr,szFields.__init__c CsP|tkrt|Sz|j|}t|t||dWStttfk rJYdSXdS)Nr-)r4r@r#r"r2r5r7r6)rrAr^Z field_valuerrrget_field_datums zFields.get_field_datumcCsRd|jkr|jSz&t|j}tdkr,|n|tfWStk rLYdSXdS)Nr r)rrtupler#rcr4r6)rrArrrrrreified_fieldss zFields.reified_fieldscs.tddfddDDS)NcSsg|]}|dk r|qSr)r)rMZ field_datumrrrrPszFields.find..csg|]}|qSr)rs)rMr^rArrrrPs)r"r+rurHrrvrrs z Fields.findcCsN|dk rJ|t|D]0}||krt|dr@|||||q|||<q|SN__call__)rur"r+hasattr)rrrr^rrrr s  z Fields.updatecCs@|dk r<|t|D]"}||kr|||r||q|Sr))rur"r+pop)rrrr^rrrrs   z Fields.filtercCsdtt|jS)N,)joinmapr3rrr1rrrrFszFields.__str__cCsd|jjdtt|jfS)Nz%s(%s)r{)r9r%r|r}reprrrr1rrrr: szFields.__repr__cCst|tot|jt|jkSr))rr2rtrrr;rrrr=#sz Fields.__eq__N) r%r&r'r(r,rsrurrrrFr:r=rrrrr2s   r2c@s@eZdZdZddZddZddZdd Zd d Zd d Z dS)rZaQ JSONPath that matches indices of the current datum, or none if not large enough. Concrete syntax is brackets. WARNING: If the datum is None or not long enough, it will not crash but will not match anything. NOTE: For the concrete syntax of `[*]`, the abstract syntax is a Slice() with no parameters (equiv to `[:]` cCs ||_dSr)index)rrrrrr,0szIndex.__init__cCs@t|}|jr8t|j|jkr8t|j|j||dgSgSdSr)r"r+r#rarrHrrrr3s z Index.findcCs>t|dr"|||j||jnt||jkr:|||j<|Srw)ryrxrrarrrrr;s   z Index.updatecCs|||jr||j|Sr))rrzrrrrrBs z Index.filtercCst|to|j|jkSr))rrZrr;rrrr=Gsz Index.__eq__cCs d|jS)Nz[%i]rr1rrrrFJsz Index.__str__N) r%r&r'r(r,rrrr=rFrrrrrZ'srZc@sJeZdZdZdddZddZddZd d Zd d Zd dZ ddZ dS)Slicea JSONPath matching a slice of an array. Because of a mismatch between JSON and XML when schema-unaware, this always returns an iterable; if the incoming data was not a list, then it returns a one element list _containing_ that data. Consider these two docs, and their schema-unaware translation to JSON: hello ==> {"a": {"b": "hello"}} hellogoodbye ==> {"a": {"b": ["hello", "goodbye"]}} If there were a schema, it would be known that "b" should always be an array (unless the schema were wonky, but that is too much to fix here) so when querying with JSON if the one writing the JSON knows that it should be an array, they can write a slice operator and it will coerce a non-array value to an array. This may be a bit unfortunate because it would be nice to always have an iterator, but dictionaries and other objects may also be iterable, so this is the compromise. NcCs||_||_||_dSr)startendstep)rrrrrrrr,fszSlice.__init__cstjsgStjts.rcs$g|]}tj|t|dqSrrYrrCrrrP{s)r"r+r#rrbsix integer_types string_typesrr r!rrrrrar`rHrrCrrks (z Slice.findcCs"||D]}|j||q |Sr)rVrRrrrr}sz Slice.updatecCsJt|}||D]"}|j||}t||krq6q|t|krqFq|Sr))rarr r)rrrlengthrArrrrs  z Slice.filtercCsZ|jdkr"|jdkr"|jdkr"dSd|jp,d|jrs(   8?1)!mC'