<s> """<STR_LIT>""" <EOL> import sys <EOL> import json <EOL> import copy <EOL> import itertools <EOL> import jsonschema <EOL> from . import records <EOL> from . import error <EOL> SCHEMA = { <EOL> '<STR_LIT>' : '<STR_LIT>' , <EOL> '<STR_LIT:title>' : '<STR_LIT>' , <EOL> '<STR_LIT:description>' : '<STR_LIT>' , <EOL> '<STR_LIT:type>' : '<STR_LIT:object>' , <EOL> '<STR_LIT>' : { <EOL> '<STR_LIT>' : { <EOL> '<STR_LIT:type>' : '<STR_LIT>' , <EOL> '<STR_LIT>' : <NUM_LIT:1> , <EOL> '<STR_LIT>' : { '<STR_LIT:type>' : '<STR_LIT:string>' } , <EOL> } , <EOL> '<STR_LIT>' : { <EOL> '<STR_LIT:type>' : '<STR_LIT>' , <EOL> '<STR_LIT>' : { '<STR_LIT:type>' : '<STR_LIT:object>' , <EOL> '<STR_LIT>' : { <EOL> '<STR_LIT>' : { '<STR_LIT:type>' : [ '<STR_LIT:string>' , '<STR_LIT>' ] } , <EOL> } } , <EOL> } , <EOL> '<STR_LIT>' : { <EOL> '<STR_LIT:type>' : '<STR_LIT>' , <EOL> '<STR_LIT>' : { '<STR_LIT:type>' : '<STR_LIT:object>' , <EOL> '<STR_LIT>' : { <EOL> '<STR_LIT>' : { '<STR_LIT:type>' : [ '<STR_LIT:string>' , '<STR_LIT>' ] } , <EOL> } } , <EOL> } , <EOL> '<STR_LIT>' : { <EOL> '<STR_LIT:type>' : '<STR_LIT>' , <EOL> '<STR_LIT>' : { <EOL> '<STR_LIT:type>' : '<STR_LIT:object>' , <EOL> '<STR_LIT>' : { <EOL> '<STR_LIT:key>' : { '<STR_LIT:type>' : '<STR_LIT>' , <EOL> '<STR_LIT>' : { '<STR_LIT:type>' : [ '<STR_LIT:string>' , '<STR_LIT>' ] } , <EOL> '<STR_LIT>' : <NUM_LIT:1> } , <EOL> '<STR_LIT>' : { <EOL> '<STR_LIT:type>' : '<STR_LIT:object>' , <EOL> '<STR_LIT>' : <NUM_LIT:1> , <EOL> '<STR_LIT>' : { <EOL> '<STR_LIT>' : { '<STR_LIT:type>' : '<STR_LIT:object>' , <EOL> '<STR_LIT>' : { <EOL> '<STR_LIT>' : { <EOL> '<STR_LIT:type>' : [ '<STR_LIT:string>' , '<STR_LIT>' ] <EOL> } , <EOL> '<STR_LIT:to>' : { <EOL> '<STR_LIT:type>' : [ '<STR_LIT:string>' , '<STR_LIT>' ] <EOL> } , <EOL> } , <EOL> '<STR_LIT>' : [ '<STR_LIT>' , '<STR_LIT:to>' ] } , <EOL> } , <EOL> } , <EOL> } , <EOL> '<STR_LIT>' : [ '<STR_LIT:key>' , '<STR_LIT>' ] , <EOL> } , <EOL> } , <EOL> } , <EOL> '<STR_LIT>' : [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' ] , <EOL> } <EOL> def is_empty ( diff ) : <EOL> "<STR_LIT>" <EOL> return not any ( [ diff [ '<STR_LIT>' ] , diff [ '<STR_LIT>' ] , diff [ '<STR_LIT>' ] ] ) <EOL> def is_valid ( diff ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> validate ( diff ) <EOL> except jsonschema . ValidationError : <EOL> return False <EOL> return True <EOL> def validate ( diff ) : <EOL> """<STR_LIT>""" <EOL> return jsonschema . validate ( diff , SCHEMA ) <EOL> def apply ( diff , recs , strict = True ) : <EOL> """<STR_LIT>""" <EOL> index_columns = diff [ '<STR_LIT>' ] <EOL> indexed = records . index ( copy . deepcopy ( list ( recs ) ) , index_columns ) <EOL> _add_records ( indexed , diff [ '<STR_LIT>' ] , index_columns , strict = strict ) <EOL> _remove_records ( indexed , diff [ '<STR_LIT>' ] , index_columns , strict = strict ) <EOL> _update_records ( indexed , diff [ '<STR_LIT>' ] , strict = strict ) <EOL> return records . sort ( indexed . values ( ) ) <EOL> def _add_records ( indexed , recs_to_add , index_columns , strict = True ) : <EOL> indexed_to_add = records . index ( recs_to_add , index_columns ) <EOL> for k , r in indexed_to_add . items ( ) : <EOL> if strict and k in indexed : <EOL> error . abort ( <EOL> '<STR_LIT>' . format ( k ) <EOL> ) <EOL> indexed [ k ] = r <EOL> def _remove_records ( indexed , recs_to_remove , index_columns , strict = True ) : <EOL> indexed_to_remove = records . index ( recs_to_remove , index_columns ) <EOL> for k , r in indexed_to_remove . items ( ) : <EOL> if strict : <EOL> v = indexed . get ( k ) <EOL> if v is None : <EOL> error . abort ( <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' . format ( k ) <EOL> ) <EOL> if v != r : <EOL> error . abort ( <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' . format ( k ) <EOL> ) <EOL> del indexed [ k ] <EOL> def _update_records ( indexed , deltas , strict = True ) : <EOL> for delta in deltas : <EOL> k = tuple ( delta [ '<STR_LIT:key>' ] ) <EOL> field_changes = delta [ '<STR_LIT>' ] <EOL> r = indexed . get ( k ) <EOL> if r is None : <EOL> if strict : <EOL> error . abort ( <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' . format ( k ) <EOL> ) <EOL> continue <EOL> r = indexed [ k ] <EOL> for field , from_to in field_changes . items ( ) : <EOL> expected = from_to [ '<STR_LIT>' ] <EOL> if strict and r . get ( field ) != expected : <EOL> error . abort ( <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' . format ( k , field ) <EOL> ) <EOL> r [ field ] = from_to [ '<STR_LIT:to>' ] <EOL> def load ( istream , strict = True ) : <EOL> "<STR_LIT>" <EOL> try : <EOL> diff = json . load ( istream ) <EOL> if strict : <EOL> jsonschema . validate ( diff , SCHEMA ) <EOL> except ValueError : <EOL> raise InvalidPatchError ( '<STR_LIT>' ) <EOL> except jsonschema . exceptions . ValidationError as e : <EOL> raise InvalidPatchError ( e . message ) <EOL> return diff <EOL> def save ( diff , stream = sys . stdout , compact = False ) : <EOL> "<STR_LIT>" <EOL> flags = { '<STR_LIT>' : True } <EOL> if not compact : <EOL> flags [ '<STR_LIT>' ] = <NUM_LIT:2> <EOL> json . dump ( diff , stream , ** flags ) <EOL> def create ( from_records , to_records , index_columns ) : <EOL> """<STR_LIT>""" <EOL> from_indexed = records . index ( from_records , index_columns ) <EOL> to_indexed = records . index ( to_records , index_columns ) <EOL> return create_indexed ( from_indexed , to_indexed , index_columns ) <EOL> def create_indexed ( from_indexed , to_indexed , index_columns ) : <EOL> removed , added , shared = _compare_keys ( from_indexed , to_indexed ) <EOL> changed = _compare_rows ( from_indexed , to_indexed , shared ) <EOL> diff = _assemble ( removed , added , changed , from_indexed , to_indexed , <EOL> index_columns ) <EOL> return diff <EOL> def _compare_keys ( from_recs , to_recs ) : <EOL> from_keys = set ( from_recs ) <EOL> to_keys = set ( to_recs ) <EOL> removed = from_keys . difference ( to_keys ) <EOL> shared = from_keys . intersection ( to_keys ) <EOL> added = to_keys . difference ( from_keys ) <EOL> return removed , added , shared <EOL> def _compare_rows ( from_recs , to_recs , keys ) : <EOL> "<STR_LIT>" <EOL> return set ( <EOL> k for k in keys <EOL> if sorted ( from_recs [ k ] . items ( ) ) != sorted ( to_recs [ k ] . items ( ) ) <EOL> ) <EOL> def _assemble ( removed , added , changed , from_recs , to_recs , index_columns ) : <EOL> diff = { } <EOL> diff [ '<STR_LIT>' ] = index_columns <EOL> diff [ '<STR_LIT>' ] = records . sort ( to_recs [ k ] for k in added ) <EOL> diff [ '<STR_LIT>' ] = records . sort ( from_recs [ k ] for k in removed ) <EOL> diff [ '<STR_LIT>' ] = sorted ( ( { '<STR_LIT:key>' : list ( k ) , <EOL> '<STR_LIT>' : record_diff ( from_recs [ k ] , to_recs [ k ] ) } <EOL> for k in changed ) , <EOL> key = _change_key ) <EOL> return diff <EOL> def _change_key ( c ) : <EOL> return tuple ( c [ '<STR_LIT:key>' ] ) <EOL> def record_diff ( lhs , rhs ) : <EOL> "<STR_LIT>" <EOL> delta = { } <EOL> for k in set ( lhs ) . union ( rhs ) : <EOL> from_ = lhs [ k ] <EOL> to_ = rhs [ k ] <EOL> if from_ != to_ : <EOL> delta [ k ] = { '<STR_LIT>' : from_ , '<STR_LIT:to>' : to_ } <EOL> return delta <EOL> def is_typed ( diff ) : <EOL> "<STR_LIT>" <EOL> return any ( type ( v ) != str for v in _iter_fields ( diff ) ) <EOL> def _iter_fields ( diff ) : <EOL> return itertools . chain ( <EOL> _iter_record_fields ( diff [ '<STR_LIT>' ] ) , <EOL> _iter_record_fields ( diff [ '<STR_LIT>' ] ) , <EOL> _iter_change_fields ( diff [ '<STR_LIT>' ] ) , <EOL> ) <EOL> def _iter_change_fields ( cs ) : <EOL> for c in cs : <EOL> for k in c [ '<STR_LIT:key>' ] : <EOL> yield k <EOL> for v in c [ '<STR_LIT>' ] . values ( ) : <EOL> yield v [ '<STR_LIT>' ] <EOL> yield v [ '<STR_LIT:to>' ] <EOL> def _iter_record_fields ( recs ) : <EOL> for r in recs : <EOL> for v in r . values ( ) : <EOL> yield v <EOL> class InvalidPatchError ( Exception ) : <EOL> pass </s>
