params_data = {<EOL>'<STR_LIT:action>': '<STR_LIT>',<EOL>}<EOL>endpoint = '<STR_LIT>'.format(milestone_request_id)<EOL>response = make_put_request(session, endpoint, params_data=params_data)<EOL>json_data = response.json()<EOL>if response.status_code == <NUM_LIT:200>:<EOL><INDENT>return json_data['<STR_LIT:status>']<EOL><DEDENT>else:<EOL><INDENT>raise MilestoneRequestNotAcceptedException(<EOL>message=json_data['<STR_LIT:message>'],<EOL>error_code=json_data['<STR_LIT>'],<EOL>request_id=json_data['<STR_LIT>'])<EOL><DEDENT>


params_data = {<EOL>'<STR_LIT:action>': '<STR_LIT>',<EOL>}<EOL>endpoint = '<STR_LIT>'.format(milestone_request_id)<EOL>response = make_put_request(session, endpoint, params_data=params_data)<EOL>json_data = response.json()<EOL>if response.status_code == <NUM_LIT:200>:<EOL><INDENT>return json_data['<STR_LIT:status>']<EOL><DEDENT>else:<EOL><INDENT>raise MilestoneRequestNotRejectedException(<EOL>message=json_data['<STR_LIT:message>'],<EOL>error_code=json_data['<STR_LIT>'],<EOL>request_id=json_data['<STR_LIT>'])<EOL><DEDENT>


params_data = {<EOL>'<STR_LIT:action>': '<STR_LIT>',<EOL>}<EOL>endpoint = '<STR_LIT>'.format(milestone_request_id)<EOL>response = make_put_request(session, endpoint, params_data=params_data)<EOL>json_data = response.json()<EOL>if response.status_code == <NUM_LIT:200>:<EOL><INDENT>return json_data['<STR_LIT:status>']<EOL><DEDENT>else:<EOL><INDENT>raise MilestoneRequestNotDeletedException(<EOL>message=json_data['<STR_LIT:message>'],<EOL>error_code=json_data['<STR_LIT>'],<EOL>request_id=json_data['<STR_LIT>'])<EOL><DEDENT>


<EOL>response = make_post_request(session, '<STR_LIT>', json_data=review)<EOL>json_data = response.json()<EOL>if response.status_code == <NUM_LIT:200>:<EOL><INDENT>return json_data['<STR_LIT:status>']<EOL><DEDENT>else:<EOL><INDENT>raise ReviewNotPostedException(<EOL>message=json_data['<STR_LIT:message>'],<EOL>error_code=json_data['<STR_LIT>'],<EOL>request_id=json_data['<STR_LIT>'])<EOL><DEDENT>


def get_jobs(session, job_ids, seo_details, lang):

get_jobs_data = {<EOL>'<STR_LIT>': job_ids,<EOL>'<STR_LIT>': seo_details,<EOL>'<STR_LIT>': lang,<EOL>}<EOL>response = make_get_request(session, '<STR_LIT>', params_data=get_jobs_data)<EOL>json_data = response.json()<EOL>if response.status_code == <NUM_LIT:200>:<EOL><INDENT>return json_data['<STR_LIT:result>']<EOL><DEDENT>else:<EOL><INDENT>raise JobsNotFoundException(<EOL>message=json_data['<STR_LIT:message>'],<EOL>error_code=json_data['<STR_LIT>'],<EOL>request_id=json_data['<STR_LIT>'])<EOL><DEDENT>


def create_thread(session, member_ids, context_type, context, message):

headers = {<EOL>'<STR_LIT:Content-Type>': '<STR_LIT>'<EOL>}<EOL>thread_data = {<EOL>'<STR_LIT>': member_ids,<EOL>'<STR_LIT>': context_type,<EOL>'<STR_LIT>': context,<EOL>'<STR_LIT:message>': message,<EOL>}<EOL>response = make_post_request(session, '<STR_LIT>', headers,<EOL>form_data=thread_data)<EOL>json_data = response.json()<EOL>if response.status_code == <NUM_LIT:200>:<EOL><INDENT>return Thread(json_data['<STR_LIT:result>'])<EOL><DEDENT>else:<EOL><INDENT>raise ThreadNotCreatedException(message=json_data['<STR_LIT:message>'],<EOL>error_code=json_data['<STR_LIT>'],<EOL>request_id=json_data['<STR_LIT>'])<EOL><DEDENT>


def create_project_thread(session, member_ids, project_id, message):

return create_thread(session, member_ids, '<STR_LIT>', project_id, message)<EOL>


def post_message(session, thread_id, message):

headers = {<EOL>'<STR_LIT:Content-Type>': '<STR_LIT>'<EOL>}<EOL>message_data = {<EOL>'<STR_LIT:message>': message,<EOL>}<EOL>endpoint = '<STR_LIT>'.format(thread_id)<EOL>response = make_post_request(session, endpoint, headers,<EOL>form_data=message_data)<EOL>json_data = response.json()<EOL>if response.status_code == <NUM_LIT:200>:<EOL><INDENT>return Message(json_data['<STR_LIT:result>'])<EOL><DEDENT>else:<EOL><INDENT>raise MessageNotCreatedException(message=json_data['<STR_LIT:message>'],<EOL>error_code=json_data['<STR_LIT>'],<EOL>request_id=json_data['<STR_LIT>'])<EOL><DEDENT>


def post_attachment(session, thread_id, attachments):

files = []<EOL>filenames = []<EOL>for attachment in attachments:<EOL><INDENT>files.append(attachment['<STR_LIT:file>'])<EOL>filenames.append(attachment['<STR_LIT:filename>'])<EOL><DEDENT>message_data = {<EOL>'<STR_LIT>': filenames,<EOL>}<EOL>endpoint = '<STR_LIT>'.format(thread_id)<EOL>response = make_post_request(session, endpoint,<EOL>form_data=message_data, files=files)<EOL>json_data = response.json()<EOL>if response.status_code == <NUM_LIT:200>:<EOL><INDENT>return Message(json_data['<STR_LIT:result>'])<EOL><DEDENT>else:<EOL><INDENT>raise MessageNotCreatedException(message=json_data['<STR_LIT:message>'],<EOL>error_code=json_data['<STR_LIT>'],<EOL>request_id=json_data['<STR_LIT>'])<EOL><DEDENT>


def get_messages(session, query, limit=<NUM_LIT:10>, offset=<NUM_LIT:0>):

query['<STR_LIT>'] = limit<EOL>query['<STR_LIT>'] = offset<EOL>response = make_get_request(session, '<STR_LIT>', params_data=query)<EOL>json_data = response.json()<EOL>if response.status_code == <NUM_LIT:200>:<EOL><INDENT>return json_data['<STR_LIT:result>']<EOL><DEDENT>else:<EOL><INDENT>raise MessagesNotFoundException(<EOL>message=json_data['<STR_LIT:message>'],<EOL>error_code=json_data['<STR_LIT>'],<EOL>request_id=json_data['<STR_LIT>']<EOL>)<EOL><DEDENT>


def search_messages(session, thread_id, query, limit=<NUM_LIT:20>,<EOL>offset=<NUM_LIT:0>, message_context_details=None,<EOL>window_above=None, window_below=None):

query = {<EOL>'<STR_LIT>': thread_id,<EOL>'<STR_LIT>': query,<EOL>'<STR_LIT>': limit,<EOL>'<STR_LIT>': offset<EOL>}<EOL>if message_context_details:<EOL><INDENT>query['<STR_LIT>'] = message_context_details<EOL><DEDENT>if window_above:<EOL><INDENT>query['<STR_LIT>'] = window_above<EOL><DEDENT>if window_below:<EOL><INDENT>query['<STR_LIT>'] = window_below<EOL><DEDENT>response = make_get_request(session, '<STR_LIT>', params_data=query)<EOL>json_data = response.json()<EOL>if response.status_code == <NUM_LIT:200>:<EOL><INDENT>return json_data['<STR_LIT:result>']<EOL><DEDENT>else:<EOL><INDENT>raise MessagesNotFoundException(<EOL>message=json_data['<STR_LIT:message>'],<EOL>error_code=json_data['<STR_LIT>'],<EOL>request_id=json_data['<STR_LIT>']<EOL>)<EOL><DEDENT>


<EOL>response = make_get_request(session, '<STR_LIT>', params_data=query)<EOL>json_data = response.json()<EOL>if response.status_code == <NUM_LIT:200>:<EOL><INDENT>return json_data['<STR_LIT:result>']<EOL><DEDENT>else:<EOL><INDENT>raise ThreadsNotFoundException(<EOL>message=json_data['<STR_LIT:message>'],<EOL>error_code=json_data['<STR_LIT>'],<EOL>request_id=json_data['<STR_LIT>']<EOL>)<EOL><DEDENT>


def wrapper(func):<EOL><INDENT>@wraps(func)<EOL>def wrapped(self, *args, **kwargs):<EOL><INDENT>return func(self, *args, **kwargs)<EOL><DEDENT>wrapped.description = description<EOL>return wrapped<EOL><DEDENT>return wrapper<EOL>


def __init__(self, methodName='<STR_LIT>', options=None):

super(TestCase, self).__init__(methodName, options)<EOL>self.reset_cookie()<EOL>self.session_id_key = self.conf_get(<EOL>SECTION_FUNKLOAD_FRIENDLY,<EOL>'<STR_LIT>',<EOL>self.__class__.session_id_key,<EOL>quiet=True)<EOL>self.site_url = self.conf_get(<EOL>'<STR_LIT>',<EOL>'<STR_LIT:url>',<EOL>self.__class__.site_url,<EOL>quiet=True)<EOL>


self.cookie = CookieDict()<EOL>


def _connect(self, url, params, ok_codes, rtype, description,<EOL>redirect=False, consumer=None):

<EOL>if self.cookie:<EOL><INDENT>self.setHeader('<STR_LIT>', self.cookie.render_to_string())<EOL><DEDENT>response = super(TestCase, self)._connect(<EOL>url, params, ok_codes, rtype, description,<EOL>redirect=redirect, consumer=consumer)<EOL>for key, value in response.headers.items():<EOL><INDENT>if key.lower() == '<STR_LIT>':<EOL><INDENT>self.cookie.from_cookie_string(value)<EOL><DEDENT><DEDENT>content_type_header = response.headers.get('<STR_LIT>')<EOL>if content_type_header:<EOL><INDENT>if '<STR_LIT:;>' in content_type_header:<EOL><INDENT>content_type, charset_pair = content_type_header.split('<STR_LIT:;>', <NUM_LIT:1>)<EOL>if '<STR_LIT:=>' in charset_pair:<EOL><INDENT>_key, charset = charset_pair.split('<STR_LIT:=>', <NUM_LIT:1>)<EOL><DEDENT>else:<EOL><INDENT>charset = None<EOL><DEDENT><DEDENT>else:<EOL><INDENT>content_type = content_type_header<EOL>charset = None<EOL><DEDENT><DEDENT>else:<EOL><INDENT>content_type = charset = None<EOL><DEDENT>response.content_type_header = content_type_header<EOL>if content_type:<EOL><INDENT>response.content_type = content_type.strip()<EOL><DEDENT>else:<EOL><INDENT>response.content_type = None<EOL><DEDENT>response.charset = charset<EOL>if response.content_type == '<STR_LIT:application/json>':<EOL><INDENT>response.data = json.loads(response.body)<EOL><DEDENT>else:<EOL><INDENT>response.data = None<EOL><DEDENT>return response<EOL>


self.cookie[self.session_id_key] = session_id<EOL>


return self.cookie.get(self.session_id_key)<EOL>


def conf_get(<EOL>self, section, key, default=FunkLoadTestCase._marker, quiet=False):

<EOL>if section == self.test_name and key == '<STR_LIT:description>':<EOL><INDENT>method = getattr(self, section, None)<EOL>if method and hasattr(method, '<STR_LIT:description>'):<EOL><INDENT>return method.description.encode('<STR_LIT:utf-8>')<EOL><DEDENT><DEDENT>if section in ['<STR_LIT>', '<STR_LIT>']:<EOL><INDENT>if section == '<STR_LIT>':<EOL><INDENT>test_name = self.__class__.__name__.lower()<EOL><DEDENT>else:  <EOL><INDENT>test_name = self.test_name<EOL><DEDENT>if key == '<STR_LIT>':<EOL><INDENT>result_directory = super(TestCase, self).conf_get(<EOL>section, '<STR_LIT>',<EOL>default=self.result_directory, quiet=quiet)<EOL>result_path = os.path.join(<EOL>result_directory,<EOL>'<STR_LIT>'.format(test_name))<EOL>return result_path<EOL><DEDENT>elif key == '<STR_LIT>':<EOL><INDENT>log_directory = super(TestCase, self).conf_get(<EOL>section, '<STR_LIT>', default=self.log_directory,<EOL>quiet=quiet)<EOL>log_to = os.path.join(<EOL>log_directory,<EOL>'<STR_LIT>'.format(test_name))<EOL>return log_to<EOL><DEDENT><DEDENT>return super(TestCase, self).conf_get(<EOL>section, key, default=default, quiet=quiet)<EOL>


def __init__(self, data, content_type='<STR_LIT:application/json>'):

Data.__init__(<EOL>self,<EOL>content_type,<EOL>json.dumps(data))<EOL>

Constructor

        :param data: data will be encoding to JSON.
        :type data: dict
        :param content_type: Content type for data
        :type content_type: str


super(CookieDict, self).__init__(*args, **kwargs)<EOL>

Constructor

        The argument is same the OrderedDict.


values = '<STR_LIT>'<EOL>for key, value in self.items():<EOL><INDENT>values += '<STR_LIT>'.format(key, value)<EOL><DEDENT>return values<EOL>


for key_value in cookie_string.split('<STR_LIT:;>'):<EOL><INDENT>if '<STR_LIT:=>' in key_value:<EOL><INDENT>key, value = key_value.split('<STR_LIT:=>', <NUM_LIT:1>)<EOL><DEDENT>else:<EOL><INDENT>key = key_value<EOL><DEDENT>strip_key = key.strip()<EOL>if strip_key and strip_key.lower() not in COOKIE_ATTRIBUTE_NAMES:<EOL><INDENT>self[strip_key] = value.strip()<EOL><DEDENT><DEDENT>


raise NotImplementedError<EOL>

Determines the next desired point in the parameter space using
the parsed data returned from the public update
method. Returns the value that will next emitted on the next
iteration using the list of dictionaries format for
arguments. If the update fails or data is None, StopIteration
should be supplied as the return value.


raise NotImplementedError<EOL>

Reset the the DynamicArgs object to its initial state and used
to reset the object adter the iterator is exhausted. The
return value is the initial argument to be returned by
next().


outputs_dir = os.path.join(info['<STR_LIT>'], '<STR_LIT>')<EOL>pattern = '<STR_LIT>' % info['<STR_LIT>']<EOL>flist = os.listdir(outputs_dir)<EOL>try:<EOL><INDENT>outputs = []<EOL>for tid in tids:<EOL><INDENT>matches = fnmatch.filter(flist, pattern.format(tid=tid))<EOL>if len(matches) != <NUM_LIT:1>:<EOL><INDENT>self.warning("<STR_LIT>" % tid)<EOL><DEDENT>contents = open(os.path.join(outputs_dir, matches[<NUM_LIT:0>]),'<STR_LIT:r>').read()<EOL>outputs.append(self.output_extractor(contents))<EOL><DEDENT>self._next_val = self._update_state(outputs)<EOL>self.trace.append((outputs, self._next_val))<EOL><DEDENT>except:<EOL><INDENT>self.warning("<STR_LIT>")<EOL>self._next_val = StopIteration<EOL><DEDENT>

Called to update the state of the iterator.  This methods
receives the set of task ids from the previous set of tasks
together with the launch information to allow the output
values to be parsed using the output_extractor. This data is then
used to determine the next desired point in the parameter
space by calling the _update_state method.


copied = self.copy()<EOL>enumerated = [el for el in enumerate(copied)]<EOL>for (group_ind, specs) in enumerated:<EOL><INDENT>if len(enumerated) > <NUM_LIT:1>: print("<STR_LIT>" % group_ind)<EOL>ordering = self.constant_keys + self.varying_keys<EOL>spec_lines = ['<STR_LIT:U+002CU+0020>'.join(['<STR_LIT>' % (k, s[k]) for k in ordering]) for s in specs]<EOL>print('<STR_LIT:\n>'.join(['<STR_LIT>' % (i,l) for (i,l) in enumerate(spec_lines)]))<EOL><DEDENT>print('<STR_LIT>' % self.__class__.__name__)<EOL>

When dynamic, not all argument values may be available.


for (i, (val, args)) in enumerate(self.trace):<EOL><INDENT>if args is StopIteration:<EOL><INDENT>info = "<STR_LIT>"<EOL><DEDENT>else:<EOL><INDENT>pprint = '<STR_LIT:U+002C>'.join('<STR_LIT:{>' + '<STR_LIT:U+002C>'.join('<STR_LIT>' % (k,v)<EOL>for (k,v) in arg.items()) + '<STR_LIT:}>' for arg in args)<EOL>info = ("<STR_LIT>" % pprint )<EOL><DEDENT>if i == <NUM_LIT:0>: print("<STR_LIT>" % (i, info))<EOL>else:      print("<STR_LIT>" % (i, info.capitalize(), val))<EOL><DEDENT>

Summarizes the trace of values used to update the DynamicArgs
and the arguments subsequently returned. May be used to
implement the summary method.


if not other: return self<EOL>dynamic = (isinstance(self, DynamicArgs),  isinstance(other, DynamicArgs))<EOL>if dynamic == (True, True):<EOL><INDENT>raise Exception('<STR_LIT>')<EOL><DEDENT>elif (True in dynamic):<EOL><INDENT>return DynamicConcatenate(self,other)<EOL><DEDENT>else:<EOL><INDENT>return Concatenate(self,other)<EOL><DEDENT>

Concatenates two argument specifiers. See Concatenate and
DynamicConcatenate documentation respectively.


if not other: return []<EOL>dynamic = (isinstance(self, DynamicArgs),  isinstance(other, DynamicArgs))<EOL>if dynamic == (True, True):<EOL><INDENT>raise Exception('<STR_LIT>')<EOL><DEDENT>elif (True in dynamic):<EOL><INDENT>return DynamicCartesianProduct(self, other)<EOL><DEDENT>else:<EOL><INDENT>return CartesianProduct(self, other)<EOL><DEDENT>

Takes the cartesian product of two argument specifiers. See
CartesianProduct and DynamicCartesianProduct documentation.


raise NotImplementedError<EOL>

Many DynamicArgs won't have a length that can be
precomputed. Most DynamicArgs objects will have an iteration
limit to guarantee eventual termination. If so, the maximum
possible number of arguments that could be generated should be
returned.


self._steps_complete += <NUM_LIT:1><EOL>if self._steps_complete == self.max_steps:<EOL><INDENT>self._termination_info = (False, self._best_val, self._arg)<EOL>return StopIteration<EOL><DEDENT>arg_inc, arg_dec = vals<EOL>best_val = min(arg_inc, arg_dec, self._best_val)<EOL>if best_val == self._best_val:<EOL><INDENT>self._termination_info = (True, best_val, self._arg)<EOL>return StopIteration<EOL><DEDENT>self._arg += self.stepsize if (arg_dec > arg_inc) else -self.stepsize<EOL>self._best_val= best_val<EOL>return [{self.key:self._arg+self.stepsize},<EOL>{self.key:self._arg-self.stepsize}]<EOL>

Takes as input a list or tuple of two elements. First the
value returned by incrementing by 'stepsize' followed by the
value returned after a 'stepsize' decrement.


def __call__(self, spec, tid=None, info={}):

raise NotImplementedError<EOL>

Formats a single argument specification supplied as a
dictionary of argument name/value pairs. The info dictionary
contains launch information as defined in the _setup_launch
method of Launcher.


def show(self, args, file_handle=None, **kwargs):

full_string = '<STR_LIT>'<EOL>info = {'<STR_LIT>':     '<STR_LIT>',<EOL>'<STR_LIT>':         '<STR_LIT>',<EOL>'<STR_LIT>':          '<STR_LIT>',<EOL>'<STR_LIT>':  '<STR_LIT>',<EOL>'<STR_LIT>':        '<STR_LIT>',<EOL>'<STR_LIT>':   '<STR_LIT>',<EOL>'<STR_LIT>':          tuple(time.localtime()),<EOL>'<STR_LIT>':       args.varying_keys,<EOL>'<STR_LIT>':      args.constant_keys,<EOL>'<STR_LIT>':     args.constant_items}<EOL>quoted_cmds = [ subprocess.list2cmdline(<EOL>[el for el in self(self._formatter(s),'<STR_LIT>',info)])<EOL>for s in args.specs]<EOL>cmd_lines = ['<STR_LIT>' % (i, qcmds) for (i,qcmds)<EOL>in enumerate(quoted_cmds)]<EOL>full_string += '<STR_LIT>'.join(cmd_lines)<EOL>if file_handle:<EOL><INDENT>file_handle.write(full_string)<EOL>file_handle.flush()<EOL><DEDENT>else:<EOL><INDENT>print(full_string)<EOL><DEDENT>

Write to file_handle if supplied, othewise print output


return<EOL>

Optional, final check that ensures valid arguments have been
passed before launch. Allows the constant and varying_keys to
be be checked and can inspect the specs attribute if an
instance of Args. If invalid, raise an Exception with the
appropriate error message, otherwise return None.


return<EOL>

Optional method that allows a Command to save state before
launch. The info argument is supplied by the Launcher.


raise NotImplementedError<EOL>

A succinct summary of the Command configuration.  Unlike the
repr, a summary does not have to be complete but must supply
key information relevant to the user. Must begin by stating
the executable.


launches = []<EOL>for path in os.listdir(self.output_dir):<EOL><INDENT>full_path = os.path.join(self.output_dir, path)<EOL>if os.path.isdir(full_path):<EOL><INDENT>launches.append(self._get_launch_info(full_path))<EOL><DEDENT><DEDENT>self.launches = sorted(launches)<EOL>

Update the launch information -- use if additional launches were
        made.


if timestamp is None: timestamp = self.timestamp<EOL>if self.timestamp_format is not None:<EOL><INDENT>root_name =  (time.strftime(self.timestamp_format, timestamp)<EOL>+ '<STR_LIT:->' + self.batch_name)<EOL><DEDENT>else:<EOL><INDENT>root_name = self.batch_name<EOL><DEDENT>path = os.path.join(self.output_directory,<EOL>*(self.subdir+[root_name]))<EOL>return os.path.abspath(path)<EOL>

A helper method that supplies the root directory name given a
timestamp.


self._spec_log += specs <EOL>log_path = os.path.join(self.root_directory, ("<STR_LIT>" % self.batch_name))<EOL>core.Log.write_log(log_path, [spec for (_, spec) in specs], allow_append=True)<EOL>

The log contains the tids and corresponding specifications
used during launch with the specifications in JSON format.


info_path = os.path.join(self.root_directory, ('<STR_LIT>' % self.batch_name))<EOL>if setup_info is None:<EOL><INDENT>try:<EOL><INDENT>with open(info_path, '<STR_LIT:r>') as info_file:<EOL><INDENT>setup_info = json.load(info_file)<EOL><DEDENT><DEDENT>except:<EOL><INDENT>setup_info = {}<EOL><DEDENT>setup_info.update({'<STR_LIT>' : tuple(time.localtime())})<EOL><DEDENT>else:<EOL><INDENT>setup_info.update({<EOL>'<STR_LIT>' : None,<EOL>'<STR_LIT>' : self.metadata<EOL>})<EOL><DEDENT>with open(info_path, '<STR_LIT:w>') as info_file:<EOL><INDENT>json.dump(setup_info, info_file, sort_keys=True, indent=<NUM_LIT:4>)<EOL><DEDENT>

All launchers should call this method to write the info file
at the end of the launch. The .info file is saved given
setup_info supplied by _setup_launch into the
root_directory. When called without setup_info, the existing
info file is updated with the end-time.


self.root_directory = self.get_root_directory()<EOL>if not os.path.isdir(self.root_directory):<EOL><INDENT>os.makedirs(self.root_directory)<EOL><DEDENT>platform_dict = {}<EOL>python_version = (platform.python_implementation()<EOL>+ platform.python_version())<EOL>platform_dict['<STR_LIT>']       = platform.platform()<EOL>platform_dict['<STR_LIT>'] = python_version<EOL>platform_dict['<STR_LIT>'] = str(lancet_version)<EOL>return {'<STR_LIT>':    self.root_directory,<EOL>'<STR_LIT>':        self.batch_name,<EOL>'<STR_LIT>':         self.tag,<EOL>'<STR_LIT>': self.description,<EOL>'<STR_LIT>':          repr(self),<EOL>'<STR_LIT>' :         platform_dict,<EOL>'<STR_LIT>':         self.timestamp,<EOL>'<STR_LIT>':  self.timestamp_format,<EOL>'<STR_LIT>':      self.args.varying_keys,<EOL>'<STR_LIT>':     self.args.constant_keys,<EOL>'<STR_LIT>':    self.args.constant_items}<EOL>

Method to be used by all launchers that prepares the root
directory and generate basic launch information for command
templates to use (including a registered timestamp).


def _launch_process_group(self, process_commands, streams_path):

processes = {}<EOL>def check_complete_processes(wait=False):<EOL><INDENT>"""<STR_LIT>"""<EOL>result = False<EOL>for proc in list(processes):<EOL><INDENT>if wait: proc.wait()<EOL>if proc.poll() is not None:<EOL><INDENT>self.debug("<STR_LIT>"<EOL>% (processes[proc]['<STR_LIT>'], proc.poll()))<EOL>processes[proc]['<STR_LIT>'].close()<EOL>processes[proc]['<STR_LIT>'].close()<EOL>del processes[proc]<EOL>result = True<EOL><DEDENT><DEDENT>return result<EOL><DEDENT>for cmd, tid in process_commands:<EOL><INDENT>self.debug("<STR_LIT>" % tid)<EOL>job_timestamp = time.strftime('<STR_LIT>')<EOL>basename = "<STR_LIT>" % (self.batch_name, job_timestamp, tid)<EOL>stdout_handle = open(os.path.join(streams_path, "<STR_LIT>"<EOL>% (basename, tid)), "<STR_LIT:wb>")<EOL>stderr_handle = open(os.path.join(streams_path, "<STR_LIT>"<EOL>% (basename, tid)), "<STR_LIT:wb>")<EOL>proc = subprocess.Popen(cmd, stdout=stdout_handle, stderr=stderr_handle)<EOL>processes[proc] = { '<STR_LIT>' : tid,<EOL>'<STR_LIT>' : stdout_handle,<EOL>'<STR_LIT>' : stderr_handle }<EOL>if self.max_concurrency:<EOL><INDENT>while len(processes) >= self.max_concurrency:<EOL><INDENT>if not check_complete_processes(len(processes)==<NUM_LIT:1>):<EOL><INDENT>time.sleep(<NUM_LIT:0.1>)<EOL><DEDENT><DEDENT><DEDENT><DEDENT>while len(processes) > <NUM_LIT:0>:<EOL><INDENT>if not check_complete_processes(True):<EOL><INDENT>time.sleep(<NUM_LIT:0.1>)<EOL><DEDENT><DEDENT>

Launches processes defined by process_commands, but only
executes max_concurrency processes at a time; if a process
completes and there are still outstanding processes to be
executed, the next processes are run until max_concurrency is
reached again.


launchinfo = self._setup_launch()<EOL>streams_path = self._setup_streams_path()<EOL>self.command.finalize(launchinfo)<EOL>self._record_info(launchinfo)<EOL>last_tid = <NUM_LIT:0><EOL>last_tids = []<EOL>for gid, groupspecs in enumerate(self.args):<EOL><INDENT>tids = list(range(last_tid, last_tid+len(groupspecs)))<EOL>last_tid += len(groupspecs)<EOL>allcommands = [self.command(<EOL>self.command._formatter(spec), tid, launchinfo)for (spec,tid) in zip(groupspecs,tids)]<EOL>self._append_log(list(zip(tids,groupspecs)))<EOL>self.message("<STR_LIT>" % (gid, len(allcommands)))<EOL>self._launch_process_group(zip(allcommands,tids), streams_path)<EOL>last_tids = tids[:]<EOL>if self.dynamic:<EOL><INDENT>self.args.update(last_tids, launchinfo)<EOL><DEDENT><DEDENT>self._record_info()<EOL>if self.reduction_fn is not None:<EOL><INDENT>self.reduction_fn(self._spec_log, self.root_directory)<EOL><DEDENT>

Call to start Launcher execution. Typically invoked by
review_and_launch but may be called directly by the user.


print("<STR_LIT>" % self.__class__.__name__)<EOL>print("<STR_LIT>" % self.batch_name)<EOL>if self.tag:<EOL><INDENT>print("<STR_LIT>" % self.tag)<EOL><DEDENT>print("<STR_LIT>" % self.get_root_directory())<EOL>print("<STR_LIT>" % self.max_concurrency)<EOL>if self.description:<EOL><INDENT>print("<STR_LIT>" % self.description)<EOL><DEDENT>

A succinct summary of the Launcher configuration.  Unlike the
repr, a summary does not have to be complete but must supply
key information relevant to the user.


def _qsub_args(self, override_options, cmd_args, append_options=[]):

opt_dict = type(self.qsub_flag_options)()<EOL>opt_dict.update(self.qsub_flag_options)<EOL>opt_dict.update(override_options)<EOL>if type(self.qsub_flag_options) == dict:   <EOL><INDENT>ordered_options = [(k, opt_dict[k]) for k in sorted(opt_dict)]<EOL><DEDENT>else:<EOL><INDENT>ordered_options =  list(opt_dict.items())<EOL><DEDENT>ordered_options += append_options<EOL>unpacked_groups = [[(k,v) for v in val] if type(val)==list else [(k,val)]<EOL>for (k,val) in ordered_options]<EOL>unpacked_kvs = [el for group in unpacked_groups for el in group]<EOL>ordered_pairs = [(k,v) if (k[<NUM_LIT:0>]=='<STR_LIT:->') else ('<STR_LIT>' % (k), v)<EOL>for (k,v) in unpacked_kvs]<EOL>ordered_options = [[k]+([v] if type(v) == str else list(v)) for (k,v) in ordered_pairs]<EOL>flattened_options = [el for kvs in ordered_options for el in kvs]<EOL>return (['<STR_LIT>'] + self.qsub_switches<EOL>+ flattened_options + [pipes.quote(c) for c in cmd_args])<EOL>

Method to generate Popen style argument list for qsub using
the qsub_switches and qsub_flag_options parameters. Switches
are returned first. The qsub_flag_options follow in keys()
ordered if not a vanilla Python dictionary (ie. a Python 2.7+
or param.external OrderedDict).  Otherwise the keys are sorted
alphanumerically. Note that override_options is a list of
key-value pairs.


self._launchinfo = self._setup_launch()<EOL>self.command.finalize(self._launchinfo)<EOL>self.job_timestamp = time.strftime('<STR_LIT>')<EOL>streams_path = self._setup_streams_path()<EOL>self.qsub_flag_options['<STR_LIT>'] = streams_path<EOL>self.qsub_flag_options['<STR_LIT>'] = streams_path<EOL>self.collate_and_launch()<EOL>self._record_info(self._launchinfo)<EOL>

Main entry point for the launcher. Collects the static
information about the launch and sets up the stdout and stderr
stream output directories. Generates the first call to
collate_and_launch().


try:   specs = next(self.spec_iter)<EOL>except StopIteration:<EOL><INDENT>self.qdel_batch()<EOL>if self.reduction_fn is not None:<EOL><INDENT>self.reduction_fn(self._spec_log, self.root_directory)<EOL><DEDENT>self._record_info()<EOL>return<EOL><DEDENT>tid_specs = [(self.last_tid + i, spec) for (i,spec) in enumerate(specs)]<EOL>self.last_tid += len(specs)<EOL>self._append_log(tid_specs)<EOL>if self.dynamic:<EOL><INDENT>self.args.update(self.last_tids, self._launchinfo)<EOL><DEDENT>self.last_tids = [tid for (tid,_) in tid_specs]<EOL>output_dir = self.qsub_flag_options['<STR_LIT>']<EOL>error_dir = self.qsub_flag_options['<STR_LIT>']<EOL>self._qsub_block(output_dir, error_dir, tid_specs)<EOL>if self.dynamic or (self.reduction_fn is not None):<EOL><INDENT>pickle_path = os.path.join(self.root_directory, '<STR_LIT>')<EOL>pickle.dump(self, open(pickle_path,'<STR_LIT:wb>'), protocol=<NUM_LIT:2>)<EOL><DEDENT>

Method that collates the previous jobs and launches the next
block of concurrent jobs when using DynamicArgs. This method
is invoked on initial launch and then subsequently via a
commandline call (to Python via qsub) to collate the
previously run jobs and launch the next block of jobs.


def _qsub_collate_and_launch(self, output_dir, error_dir, job_names):

job_name = "<STR_LIT>" % (self.batch_name,<EOL>self.job_timestamp,<EOL>self.collate_count)<EOL>overrides = [("<STR_LIT>",error_dir), ('<STR_LIT>',job_name), ("<STR_LIT>",output_dir),<EOL>('<STR_LIT>','<STR_LIT:U+002C>'.join(job_names))]<EOL>resume_cmds =["<STR_LIT>",<EOL>("<STR_LIT>"<EOL>% self.root_directory),<EOL>"<STR_LIT>",<EOL>"<STR_LIT>"]<EOL>cmd_args = [self.command.executable,<EOL>'<STR_LIT:-c>', '<STR_LIT:;>'.join(resume_cmds)]<EOL>popen_args = self._qsub_args(overrides, cmd_args)<EOL>p = subprocess.Popen(popen_args, stdout=subprocess.PIPE)<EOL>(stdout, stderr) = p.communicate()<EOL>self.debug(stdout)<EOL>if p.poll() != <NUM_LIT:0>:<EOL><INDENT>raise EnvironmentError("<STR_LIT>" % p.poll())<EOL><DEDENT>self.collate_count += <NUM_LIT:1><EOL>self.message("<STR_LIT>")<EOL>return job_name<EOL>

The method that actually runs qsub to invoke the python
process with the necessary commands to trigger the next
collation step and next block of jobs.


def _qsub_block(self, output_dir, error_dir, tid_specs):

processes = []<EOL>job_names = []<EOL>for (tid, spec) in tid_specs:<EOL><INDENT>job_name = "<STR_LIT>" % (self.batch_name, self.job_timestamp, tid)<EOL>job_names.append(job_name)<EOL>cmd_args = self.command(<EOL>self.command._formatter(spec),<EOL>tid, self._launchinfo)<EOL>popen_args = self._qsub_args([("<STR_LIT>",error_dir), ('<STR_LIT>',job_name), ("<STR_LIT>",output_dir)],<EOL>cmd_args)<EOL>p = subprocess.Popen(popen_args, stdout=subprocess.PIPE)<EOL>(stdout, stderr) = p.communicate()<EOL>self.debug(stdout)<EOL>if p.poll() != <NUM_LIT:0>:<EOL><INDENT>raise EnvironmentError("<STR_LIT>" % p.poll())<EOL><DEDENT>processes.append(p)<EOL><DEDENT>self.message("<STR_LIT>" % len(processes))<EOL>if (self.reduction_fn is not None) or self.dynamic:<EOL><INDENT>self._qsub_collate_and_launch(output_dir, error_dir, job_names)<EOL><DEDENT>

This method handles static argument specifiers and cases where
the dynamic specifiers cannot be queued before the arguments
are known.


p = subprocess.Popen(['<STR_LIT>', '<STR_LIT>' % (self.batch_name,<EOL>self.job_timestamp)],<EOL>stdout=subprocess.PIPE)<EOL>(stdout, stderr) = p.communicate()<EOL>return p.poll()<EOL>

Runs qdel command to remove all remaining queued jobs using
the <batch_name>* pattern . Necessary when StopIteration is
raised with scheduled jobs left on the queue.
Returns exit-code of qdel.


processes = []<EOL>for cmd, tid in process_commands:<EOL><INDENT>job_timestamp = time.strftime('<STR_LIT>')<EOL>basename = "<STR_LIT>" % (self.batch_name, job_timestamp, tid)<EOL>stdout_path = os.path.join(streams_path, "<STR_LIT>" % (basename, tid))<EOL>stderr_path = os.path.join(streams_path, "<STR_LIT>" % (basename, tid))<EOL>process = { '<STR_LIT>' : tid,<EOL>'<STR_LIT>' : cmd,<EOL>'<STR_LIT>' : stdout_path,<EOL>'<STR_LIT>' : stderr_path }<EOL>processes.append(process)<EOL><DEDENT>json_path = os.path.join(self.root_directory, self.json_name % (tid))<EOL>with open(json_path, '<STR_LIT:w>') as json_file:<EOL><INDENT>json.dump(processes, json_file, sort_keys=True, indent=<NUM_LIT:4>)<EOL><DEDENT>p = subprocess.Popen([self.script_path, json_path, self.batch_name,<EOL>str(len(processes)), str(self.max_concurrency)])<EOL>if p.wait() != <NUM_LIT:0>:<EOL><INDENT>raise EnvironmentError("<STR_LIT>" % p.poll())<EOL><DEDENT>

Aggregates all process_commands and the designated output files into a
list, and outputs it as JSON, after which the wrapper script is called.


if len(launchers) == <NUM_LIT:0>: raise Exception('<STR_LIT>')<EOL>timestamps = [launcher.timestamp for launcher in launchers]<EOL>if not all(timestamps[<NUM_LIT:0>] == tstamp for tstamp in timestamps):<EOL><INDENT>raise Exception("<STR_LIT>"<EOL>"<STR_LIT>")<EOL><DEDENT>root_directories = []<EOL>for launcher in launchers:<EOL><INDENT>command = launcher.command<EOL>args = launcher.args<EOL>command.verify(args)<EOL>root_directory = launcher.get_root_directory()<EOL>if os.path.isdir(root_directory):<EOL><INDENT>raise Exception("<STR_LIT>" % root_directory)<EOL><DEDENT>if root_directory in root_directories:<EOL><INDENT>raise Exception("<STR_LIT>")<EOL><DEDENT>root_directories.append(root_directory)<EOL><DEDENT>

Performs consistency checks across all the launchers.


for launcher in launchers:<EOL><INDENT>print("<STR_LIT>" % launcher.batch_name)<EOL>launcher()<EOL><DEDENT>return True<EOL>


<EOL>if self.launch_args is not None:<EOL><INDENT>proceed = self.review_args(self.launch_args,<EOL>show_repr=True,<EOL>heading='<STR_LIT>')<EOL>if not proceed: return False<EOL><DEDENT>reviewers = [self.review_args,<EOL>self.review_command,<EOL>self.review_launcher]<EOL>for (count, launcher) in enumerate(launchers):<EOL><INDENT>if not all(reviewer(launcher) for reviewer in reviewers):<EOL><INDENT>print("<STR_LIT>")<EOL>return False<EOL><DEDENT>if len(launchers)!= <NUM_LIT:1> and count < len(launchers)-<NUM_LIT:1>:<EOL><INDENT>skip_remaining = self.input_options(['<STR_LIT:Y>', '<STR_LIT:n>','<STR_LIT>'],<EOL>'<STR_LIT>', default='<STR_LIT:y>')<EOL>if skip_remaining == '<STR_LIT:y>':          break<EOL>elif skip_remaining == '<STR_LIT>':     return False<EOL><DEDENT><DEDENT>if self.input_options(['<STR_LIT:y>','<STR_LIT:N>'], '<STR_LIT>', default='<STR_LIT:n>') != '<STR_LIT:y>':<EOL><INDENT>return False<EOL><DEDENT>else:<EOL><INDENT>return self._launch_all(launchers)<EOL><DEDENT>

Runs the review process for all the launchers.


def review_args(self, obj, show_repr=False, heading='<STR_LIT>'):

args = obj.args if isinstance(obj, Launcher) else obj<EOL>print('<STR_LIT>' % self.summary_heading(heading))<EOL>args.summary()<EOL>if show_repr: print("<STR_LIT>" % args)<EOL>response = self.input_options(['<STR_LIT:y>', '<STR_LIT:N>','<STR_LIT>'],<EOL>'<STR_LIT>', default='<STR_LIT:n>')<EOL>if response == '<STR_LIT>': return False<EOL>if response == '<STR_LIT:y>':  args.show()<EOL>print('<STR_LIT>')<EOL>return True<EOL>

Reviews the given argument specification. Can review the
meta-arguments (launch_args) or the arguments themselves.


def input_options(self, options, prompt='<STR_LIT>', default=None):

check_options = [x.lower() for x in options]<EOL>while True:<EOL><INDENT>response = input('<STR_LIT>' % (prompt, '<STR_LIT:U+002CU+0020>'.join(options))).lower()<EOL>if response in check_options: return response.strip()<EOL>elif response == '<STR_LIT>' and default is not None:<EOL><INDENT>return default.lower().strip()<EOL><DEDENT><DEDENT>

Helper to prompt the user for input on the commandline.


def save(self, filename,  metadata={}, **data):

intersection = set(metadata.keys()) & set(data.keys())<EOL>if intersection:<EOL><INDENT>msg = '<STR_LIT>'<EOL>raise Exception(msg  % '<STR_LIT:U+002C>'.join(intersection))<EOL><DEDENT>

The implementation in the base class simply checks there is no
clash between the metadata and data keys.


raise NotImplementedError<EOL>


(basename, ext) = os.path.splitext(filename)<EOL>basename = basename if (ext in self.extensions) else filename<EOL>ext = ext if (ext in self.extensions) else self.extensions[<NUM_LIT:0>]<EOL>savepath = os.path.abspath(os.path.join(self.directory,<EOL>'<STR_LIT>' % (basename, ext)))<EOL>return (tempfile.mkstemp(ext, basename + "<STR_LIT:_>", self.directory)[<NUM_LIT:1>]<EOL>if self.hash_suffix else savepath)<EOL>

Returns the full path for saving the file, adding an extension
and making the filename unique as necessary.


if not isinstance(filename, str):<EOL><INDENT>return False<EOL><DEDENT>(_, ext) = os.path.splitext(filename)<EOL>if ext not in cls.extensions:<EOL><INDENT>return False<EOL><DEDENT>else:<EOL><INDENT>return True<EOL><DEDENT>

Returns a boolean indicating whether the filename has an
appropriate extension for this class.


def save(self, filename, imdata, **data):

if isinstance(imdata, numpy.ndarray):<EOL><INDENT>imdata = Image.fromarray(numpy.uint8(imdata))<EOL><DEDENT>elif isinstance(imdata, Image.Image):<EOL><INDENT>imdata.save(self._savepath(filename))<EOL><DEDENT>

Data may be either a PIL Image object or a Numpy array.


global _loaded<EOL>if not _loaded:<EOL><INDENT>_loaded = True<EOL>from lancet import launch<EOL>if sys.version_info[<NUM_LIT:0>] == <NUM_LIT:2>:<EOL><INDENT>launch.input = lambda *args, **kwargs: raw_input(*args, **kwargs)<EOL><DEDENT>plaintext_formatter = ip.display_formatter.formatters['<STR_LIT>']<EOL>plaintext_formatter.for_type(Args, repr_pretty_annotated)<EOL>plaintext_formatter.for_type(Command, repr_pretty_unannotated)<EOL>plaintext_formatter.for_type(Launcher, repr_pretty_unannotated)<EOL>plaintext_formatter.for_type(FileType, repr_pretty_unannotated)<EOL>plaintext_formatter.for_type(review_and_launch, repr_pretty_unannotated)<EOL><DEDENT>

IPython pretty printing support (optional). To load the extension
you may execute the following in IPython:

%load_ext lancet


def __call__(self, paths=[], **params_to_override):

p=param.ParamOverrides(self, dict(params_to_override, paths=paths))<EOL>if p.paths == []:<EOL><INDENT>raise Exception("<STR_LIT>")<EOL><DEDENT>paths = [p.paths] if isinstance(p.paths, str) else p.paths<EOL>def _desc(path, ind):<EOL><INDENT>for vcs in p.commands.keys():<EOL><INDENT>if os.path.exists(os.path.join(path, vcs)):<EOL><INDENT>proc = subprocess.Popen(p.commands[vcs][ind],<EOL>stdout=subprocess.PIPE,<EOL>stderr=subprocess.PIPE, cwd=path)<EOL>return str(proc.communicate()[<NUM_LIT:0>].decode()).strip()<EOL><DEDENT><DEDENT><DEDENT>abspaths = [os.path.abspath(path) for path in paths]<EOL>return {'<STR_LIT>' : dict((path, _desc(path,<NUM_LIT:0>)) for path in abspaths),<EOL>'<STR_LIT>':  dict((path, _desc(path,<NUM_LIT:1>)) for path in abspaths),<EOL>'<STR_LIT>':     dict((path, _desc(path,<NUM_LIT:2>)) for path in abspaths)}<EOL>

Takes a single path string or a list of path strings and
returns the corresponing version control information.


Arguments.set_default('<STR_LIT>', value)<EOL>

Function to set the floating precision across lancet.


if not Table:<EOL><INDENT>return "<STR_LIT>"<EOL><DEDENT>kdims = [dim for dim in args.constant_keys + args.varying_keys<EOL>if dim not in vdims]<EOL>items = [tuple([spec[k] for k in kdims+vdims])<EOL>for spec in args.specs]<EOL>return Table(items, kdims=kdims, vdims=vdims)<EOL>

Helper function to convet an Args object to a HoloViews Table


def pprint_args(self, pos_args, keyword_args, infix_operator=None, extra_params={}):

if infix_operator and not (len(pos_args)==<NUM_LIT:2> and keyword_args==[]):<EOL><INDENT>raise Exception('<STR_LIT>'<EOL>'<STR_LIT>')<EOL><DEDENT>(kwargs,_,_,_) = self._pprint_args<EOL>self._pprint_args = (keyword_args + kwargs, pos_args, infix_operator, extra_params)<EOL>

Method to define the positional arguments and keyword order
for pretty printing.


def _pprint(self, cycle=False, flat=False, annotate=False, onlychanged=True, level=<NUM_LIT:1>, tab = '<STR_LIT:U+0020>'):

(kwargs, pos_args, infix_operator, extra_params) = self._pprint_args<EOL>(br, indent)  = ('<STR_LIT>' if flat else '<STR_LIT:\n>', '<STR_LIT>' if flat else tab * level)<EOL>prettify = lambda x: isinstance(x, PrettyPrinted) and not flat<EOL>pretty = lambda x: x._pprint(flat=flat, level=level+<NUM_LIT:1>) if prettify(x) else repr(x)<EOL>params = dict(self.get_param_values())<EOL>show_lexsort = getattr(self, '<STR_LIT>', None) is not None<EOL>modified = [k for (k,v) in self.get_param_values(onlychanged=onlychanged)]<EOL>pkwargs = [(k, params[k])  for k in kwargs if (k in modified)] + list(extra_params.items())<EOL>arg_list = [(k,params[k]) for k in pos_args] + pkwargs<EOL>lines = []<EOL>if annotate: <EOL><INDENT>len_ckeys, len_vkeys = len(self.constant_keys), len(self.varying_keys)<EOL>info_triple = (len(self),<EOL>'<STR_LIT>' % len_ckeys if len_ckeys else '<STR_LIT>',<EOL>'<STR_LIT>'  % len_vkeys if len_vkeys else '<STR_LIT>')<EOL>annotation = '<STR_LIT>' % info_triple<EOL>lines = [annotation]<EOL><DEDENT>if show_lexsort: lines.append('<STR_LIT:(>')<EOL>if cycle:<EOL><INDENT>lines.append('<STR_LIT>' % self.__class__.__name__)<EOL><DEDENT>elif infix_operator:<EOL><INDENT>level = level - <NUM_LIT:1><EOL>triple = (pretty(params[pos_args[<NUM_LIT:0>]]), infix_operator, pretty(params[pos_args[<NUM_LIT:1>]]))<EOL>lines.append('<STR_LIT>' % triple)<EOL><DEDENT>else:<EOL><INDENT>lines.append('<STR_LIT>' % self.__class__.__name__)<EOL>for (k,v) in arg_list:<EOL><INDENT>lines.append('<STR_LIT>' %  (br+indent, k, pretty(v)))<EOL>lines.append('<STR_LIT:U+002C>')<EOL><DEDENT>lines = lines[:-<NUM_LIT:1>] +[br+(tab*(level-<NUM_LIT:1>))+'<STR_LIT:)>'] <EOL><DEDENT>if show_lexsort:<EOL><INDENT>lines.append('<STR_LIT>' % '<STR_LIT:U+002CU+0020>'.join(repr(el) for el in self._lexorder))<EOL><DEDENT>return '<STR_LIT>'.join(lines)<EOL>

Pretty printer that prints only the modified keywords and
generates flat representations (for repr) and optionally
annotates the top of the repr with a comment.


return type(spec)((k, str(v)) for (k,v) in spec.items())<EOL>

Formats the elements of an argument set appropriately


raise NotImplementedError<EOL>

Returns the list of parameter names whose values are constant
as the argument specifier is iterated.  Note that the union of
constant and varying_keys should partition the entire set of
keys in the case where there are no unsortable keys.


raise NotImplementedError<EOL>

Returns the set of constant items as a list of tuples. This
allows easy conversion to dictionary format. Note, the items
should be supplied in the same key ordering as for
constant_keys for consistency.


raise NotImplementedError<EOL>

Returns the list of parameters whose values vary as the
argument specifier is iterated.  Whenever it is possible, keys
should be sorted from those slowest to faster varying and
sorted alphanumerically within groups that vary at the same
rate.


raise StopIteration<EOL>

Called to get a list of specifications: dictionaries with
parameter name keys and string values.


return copy.copy(self)<EOL>

Convenience method to avoid using the specifier without
exhausting it.


<EOL>allkeys = itertools.chain.from_iterable(<EOL>[[(k, run[k]) for k in run] for run in specs])<EOL>collection = defaultdict(list)<EOL>for (k,v) in allkeys: collection[k].append(v)<EOL>return collection<EOL>

Returns a dictionary like object with the lists of values
collapsed by their respective key. Useful to find varying vs
constant keys and to find how fast keys vary.


return self._operator(Concatenate, other)<EOL>


return self._operator(CartesianProduct, other)<EOL>

Takes the Cartesian product of two argument specifiers.


def _cartesian_product(self, first_specs, second_specs):

return  [ dict(zip(<EOL>list(s1.keys()) + list(s2.keys()),<EOL>list(s1.values()) + list(s2.values())<EOL>))<EOL>for s1 in first_specs for s2 in second_specs ]<EOL>

Takes the Cartesian product of the specifications. Result will
contain N specifications where N = len(first_specs) *
len(second_specs) and keys are merged.
Example: [{'a':1},{'b':2}] * [{'c':3},{'d':4}] =
[{'a':1,'c':3},{'a':1,'d':4},{'b':2,'c':3},{'b':2,'d':4}]


print("<STR_LIT>" % len(self))<EOL>varying_keys = '<STR_LIT:U+002CU+0020>'.join('<STR_LIT>' % k for k in self.varying_keys)<EOL>print("<STR_LIT>" % varying_keys)<EOL>items = '<STR_LIT:U+002CU+0020>'.join(['<STR_LIT>' % (k,v)<EOL>for (k,v) in self.constant_items])<EOL>if self.constant_items:<EOL><INDENT>print("<STR_LIT>" % items)<EOL><DEDENT>

A succinct summary of the argument specifier. Unlike the repr,
a summary does not have to be complete but must supply the
most relevant information about the object to the user.


def _build_specs(self, specs, kwargs, fp_precision):

if specs is None:<EOL><INDENT>overrides = param.ParamOverrides(self, kwargs,<EOL>allow_extra_keywords=True)<EOL>extra_kwargs = overrides.extra_keywords()<EOL>kwargs = dict([(k,v) for (k,v) in kwargs.items()<EOL>if k not in extra_kwargs])<EOL>rounded_specs = list(self.round_floats([extra_kwargs],<EOL>fp_precision))<EOL>if extra_kwargs=={}: return [], kwargs, True<EOL>else:                return rounded_specs, kwargs, False<EOL><DEDENT>return list(self.round_floats(specs, fp_precision)), kwargs, True<EOL>

Returns the specs, the remaining kwargs and whether or not the
constructor was called with kwarg or explicit specs.


def _unique(self, sequence, idfun=repr):

seen = {}<EOL>return [seen.setdefault(idfun(e),e) for e in sequence<EOL>if idfun(e) not in seen]<EOL>

Note: repr() must be implemented properly on all objects. This
is implicitly assumed by Lancet when Python objects need to be
formatted to string representation.


ordering = self.constant_keys + self.varying_keys<EOL>spec_lines = ['<STR_LIT:U+002CU+0020>'.join(['<STR_LIT>' % (k, s[k]) for k in ordering<EOL>if (k in s) and (k not in exclude)])<EOL>for s in self.specs]<EOL>print('<STR_LIT:\n>'.join(['<STR_LIT>' % (i,l) for (i,l) in enumerate(spec_lines)]))<EOL>

Convenience method to inspect the available argument values in
human-readable format. The ordering of keys is determined by
how quickly they vary.

The exclude list allows specific keys to be excluded for
readability (e.g. to hide long, absolute filenames).


if order == []:<EOL><INDENT>raise Exception("<STR_LIT>"<EOL>"<STR_LIT>"<EOL>"<STR_LIT>")<EOL><DEDENT>if not set(el[<NUM_LIT:1>:] for el in order).issubset(set(self.varying_keys)):<EOL><INDENT>raise Exception("<STR_LIT>")<EOL><DEDENT>sorted_args = copy.deepcopy(self)<EOL>specs_param = sorted_args.params('<STR_LIT>')<EOL>specs_param.constant = False<EOL>sorted_args.specs = self._lexsorted_specs(order)<EOL>specs_param.constant = True<EOL>sorted_args._lexorder = order<EOL>return sorted_args<EOL>

The lexical sort order is specified by a list of string
arguments. Each string is a key name prefixed by '+' or '-'
for ascending and descending sort respectively. If the key is
not found in the operand's set of varying keys, it is ignored.


specs = self.specs[:]<EOL>if not all(el[<NUM_LIT:0>] in ['<STR_LIT:+>', '<STR_LIT:->'] for el in order):<EOL><INDENT>raise Exception("<STR_LIT>"<EOL>"<STR_LIT>"<EOL>"<STR_LIT>")<EOL><DEDENT>sort_cycles = [(el[<NUM_LIT:1>:], True if el[<NUM_LIT:0>]=='<STR_LIT:+>' else False)<EOL>for el in reversed(order)<EOL>if el[<NUM_LIT:1>:] in self.varying_keys]<EOL>for (key, ascending) in sort_cycles:<EOL><INDENT>specs = sorted(specs, key=lambda s: s.get(key, None),<EOL>reverse=(not ascending))<EOL><DEDENT>return specs<EOL>

A lexsort is specified using normal key string prefixed by '+'
(for ascending) or '-' for (for descending).

Note that in Python 2, if a key is missing, None is returned
(smallest Python value). In Python 3, an Exception will be
raised regarding comparison of heterogenous types.


if n == <NUM_LIT:1>: return [start]<EOL>L = [<NUM_LIT:0.0>] * n<EOL>nm1 = n - <NUM_LIT:1><EOL>nm1inv = <NUM_LIT:1.0> / nm1<EOL>for i in range(n):<EOL><INDENT>L[i] = nm1inv * (start*(nm1 - i) + stop*i)<EOL><DEDENT>return L<EOL>


log_path = (log_path if os.path.isfile(log_path)<EOL>else os.path.join(os.getcwd(), log_path))<EOL>with open(log_path,'<STR_LIT:r>') as log:<EOL><INDENT>splits = (line.split() for line in log)<EOL>uzipped = ((int(split[<NUM_LIT:0>]), json.loads("<STR_LIT:U+0020>".join(split[<NUM_LIT:1>:]))) for split in splits)<EOL>szipped = [(i, dict((str(k),v) for (k,v) in d.items())) for (i,d) in uzipped]<EOL><DEDENT>return dict_type(szipped)<EOL>

Parses the log file generated by a launcher and returns
dictionary with tid keys and specification values.

Ordering can be maintained by setting dict_type to the
appropriate constructor (i.e. OrderedDict). Keys are converted
from unicode to strings for kwarg use.


@staticmethod<EOL><INDENT>def write_log(log_path, data, allow_append=True):<DEDENT>

append = os.path.isfile(log_path)<EOL>islist = isinstance(data, list)<EOL>if append and not allow_append:<EOL><INDENT>raise Exception('<STR_LIT>'<EOL>'<STR_LIT>' % log_path)<EOL><DEDENT>if not (islist or isinstance(data, Args)):<EOL><INDENT>raise Exception('<STR_LIT>'<EOL>'<STR_LIT>')<EOL><DEDENT>specs = data if islist else data.specs<EOL>if not all(isinstance(el,dict) for el in specs):<EOL><INDENT>raise Exception('<STR_LIT>')<EOL><DEDENT>log_file = open(log_path, '<STR_LIT>') if append else open(log_path, '<STR_LIT:w>')<EOL>start = int(log_file.readlines()[-<NUM_LIT:1>].split()[<NUM_LIT:0>])+<NUM_LIT:1> if append else <NUM_LIT:0><EOL>ascending_indices = range(start, start+len(data))<EOL>log_str = '<STR_LIT:\n>'.join(['<STR_LIT>' % (tid, json.dumps(el))<EOL>for (tid, el) in zip(ascending_indices,specs)])<EOL>log_file.write("<STR_LIT:\n>"+log_str if append else log_str)<EOL>log_file.close()<EOL>

Writes the supplied specifications to the log path. The data
may be supplied as either as a an Args or as a list of
dictionaries.

By default, specifications will be appropriately appended to
an existing log file. This can be disabled by setting
allow_append to False.


@classmethod<EOL><INDENT>def directory(cls, directory, root=None, extension=None, **kwargs):<DEDENT>

root = os.getcwd() if root is None else root<EOL>suffix = '<STR_LIT>' if extension is None else '<STR_LIT:.>' + extension.rsplit('<STR_LIT:.>')[-<NUM_LIT:1>]<EOL>pattern = directory + os.sep + '<STR_LIT:*>' + suffix<EOL>key = os.path.join(root, directory,'<STR_LIT:*>').rsplit(os.sep)[-<NUM_LIT:2>]<EOL>format_parse = list(string.Formatter().parse(key))<EOL>if not all([el is None for el in zip(*format_parse)[<NUM_LIT:1>]]):<EOL><INDENT>raise Exception('<STR_LIT>')<EOL><DEDENT>return cls(key, pattern, root, **kwargs)<EOL>

Load all the files in a given directory selecting only files
with the given extension if specified. The given kwargs are
passed through to the normal constructor.


parse = list(string.Formatter().parse(self.pattern))<EOL>return [f for f in zip(*parse)[<NUM_LIT:1>] if f is not None]<EOL>

Return the fields specified in the pattern using Python's
formatting mini-language.


def _load_expansion(self, key, root, pattern):

path_pattern = os.path.join(root, pattern)<EOL>expanded_paths = self._expand_pattern(path_pattern)<EOL>specs=[]<EOL>for (path, tags) in expanded_paths:<EOL><INDENT>filelist = [os.path.join(path,f) for f in os.listdir(path)] if os.path.isdir(path) else [path]<EOL>for filepath in filelist:<EOL><INDENT>specs.append(dict(tags,**{key:os.path.abspath(filepath)}))<EOL><DEDENT><DEDENT>return sorted(specs, key=lambda s: s[key])<EOL>

Loads the files that match the given pattern.


(globpattern, regexp, fields, types) = self._decompose_pattern(pattern)<EOL>filelist = glob.glob(globpattern)<EOL>expansion = []<EOL>for fname in filelist:<EOL><INDENT>if fields == []:<EOL><INDENT>expansion.append((fname, {}))<EOL>continue<EOL><DEDENT>match = re.match(regexp, fname)<EOL>if match is None: continue<EOL>match_items = match.groupdict().items()<EOL>tags = dict((k,types.get(k, str)(v)) for (k,v) in match_items)<EOL>expansion.append((fname, tags))<EOL><DEDENT>return expansion<EOL>

From the pattern decomposition, finds the absolute paths
matching the pattern.


sep = '<STR_LIT>'<EOL>float_codes = ['<STR_LIT:e>','<STR_LIT:E>','<STR_LIT:f>', '<STR_LIT:F>','<STR_LIT:g>', '<STR_LIT>', '<STR_LIT:n>']<EOL>typecodes = dict([(k,float) for k in float_codes]<EOL>+ [('<STR_LIT:b>',bin), ('<STR_LIT:d>',int), ('<STR_LIT:o>',oct), ('<STR_LIT:x>',hex)])<EOL>parse = list(string.Formatter().parse(pattern))<EOL>text, fields, codes, _ = zip(*parse)<EOL>types = []<EOL>for (field, code) in zip(fields, codes):<EOL><INDENT>if code in ['<STR_LIT>', None]: continue<EOL>constructor =  typecodes.get(code[-<NUM_LIT:1>], None)<EOL>if constructor: types += [(field, constructor)]<EOL><DEDENT>stars =  ['<STR_LIT>' if not f else '<STR_LIT:*>' for f in fields]<EOL>globpat = '<STR_LIT>'.join(text+star for (text,star) in zip(text,stars))<EOL>refields = ['<STR_LIT>' if not f else sep+('<STR_LIT>'% f)+sep for f in fields]<EOL>parts = '<STR_LIT>'.join(text+group for (text,group) in zip(text, refields)).split(sep)<EOL>for i in range(<NUM_LIT:0>, len(parts), <NUM_LIT:2>): parts[i] = re.escape(parts[i])<EOL>regexp_pattern = '<STR_LIT>'.join(parts).replace('<STR_LIT>','<STR_LIT>')<EOL>fields = list(f for f in fields if f)<EOL>return globpat, regexp_pattern , fields, dict(types)<EOL>

Given a path pattern with format declaration, generates a
four-tuple (glob_pattern, regexp pattern, fields, type map)


@classmethod<EOL><INDENT>def from_pattern(cls, pattern, filetype=None, key='<STR_LIT:filename>', root=None, ignore=[]):<DEDENT>

filepattern = FilePattern(key, pattern, root=root)<EOL>if FileInfo.filetype and filetype is None:<EOL><INDENT>filetype = FileInfo.filetype<EOL><DEDENT>elif filetype is None:<EOL><INDENT>raise Exception("<STR_LIT>"<EOL>"<STR_LIT>"<EOL>"<STR_LIT>")<EOL><DEDENT>return FileInfo(filepattern, key, filetype, ignore=ignore)<EOL>

Convenience method to directly chain a pattern processed by
FilePattern into a FileInfo instance.

Note that if a default filetype has been set on FileInfo, the
filetype argument may be omitted.


if Table and isinstance(val, Table):<EOL><INDENT>return self.load_table(val, **kwargs)<EOL><DEDENT>elif DataFrame and isinstance(val, DataFrame):<EOL><INDENT>return self.load_dframe(val, **kwargs)<EOL><DEDENT>else:<EOL><INDENT>raise Exception("<STR_LIT>" % type(val))<EOL><DEDENT>

Load the file contents into the supplied pandas dataframe or
HoloViews Table. This allows a selection to be made over the
metadata before loading the file contents (may be slow).


items,  data_keys = [], None<EOL>for key, filename in table.items():<EOL><INDENT>data_dict = self.filetype.data(filename[<NUM_LIT:0>])<EOL>current_keys = tuple(sorted(data_dict.keys()))<EOL>values = [data_dict[k] for k in current_keys]<EOL>if data_keys is None:<EOL><INDENT>data_keys = current_keys<EOL><DEDENT>elif data_keys != current_keys:<EOL><INDENT>raise Exception("<STR_LIT>")<EOL><DEDENT>items.append((key, values))<EOL><DEDENT>return Table(items, kdims=table.kdims, vdims=data_keys)<EOL>

Load the file contents into the supplied Table using the
specified key and filetype. The input table should have the
filenames as values which will be replaced by the loaded
data. If data_key is specified, this key will be used to index
the loaded data to retrive the specified item.


filename_series = dframe[self.key]<EOL>loaded_data = filename_series.map(self.filetype.data)<EOL>keys = [list(el.keys()) for el in loaded_data.values]<EOL>for key in set().union(*keys):<EOL><INDENT>key_exists = key in dframe.columns<EOL>if key_exists:<EOL><INDENT>self.warning("<STR_LIT>"<EOL>"<STR_LIT>" % key)<EOL><DEDENT>suffix = '<STR_LIT>' if key_exists else '<STR_LIT>'<EOL>dframe[key+suffix] = loaded_data.map(lambda x: x.get(key, np.nan))<EOL><DEDENT>return dframe<EOL>

Load the file contents into the supplied dataframe using the
specified key and filetype.


def _info(self, source, key, filetype, ignore):

specs, mdata = [], {}<EOL>mdata_clashes  = set()<EOL>for spec in source.specs:<EOL><INDENT>if key not in spec:<EOL><INDENT>raise Exception("<STR_LIT>" % key)<EOL><DEDENT>mdata = dict((k,v) for (k,v) in filetype.metadata(spec[key]).items()<EOL>if k not in ignore)<EOL>mdata_spec = {}<EOL>mdata_spec.update(spec)<EOL>mdata_spec.update(mdata)<EOL>specs.append(mdata_spec)<EOL>mdata_clashes = mdata_clashes | (set(spec.keys()) & set(mdata.keys()))<EOL><DEDENT>if mdata_clashes:<EOL><INDENT>self.warning("<STR_LIT>")<EOL><DEDENT>return specs<EOL>

Generates the union of the source.specs and the metadata
dictionary loaded by the filetype object.


if not os.path.isdir(path):<EOL><INDENT>raise Exception("<STR_LIT>" % path)<EOL><DEDENT>else:<EOL><INDENT>assets = os.listdir(path)<EOL>if len(assets) == <NUM_LIT:0>:<EOL><INDENT>raise Exception("<STR_LIT>" % path)<EOL><DEDENT><DEDENT>

Verifies that a fake subpackage path for assets (notebooks, svgs,
pngs etc) both exists and is populated with files.


partners = dict((a, (rankings[(a, <NUM_LIT:1>)], <NUM_LIT:1>)) for a in A)<EOL>is_stable = False <EOL>while is_stable == False:<EOL><INDENT>is_stable = True<EOL>for b in B:<EOL><INDENT>is_paired = False <EOL>for n in range(<NUM_LIT:1>, len(B) + <NUM_LIT:1>):<EOL><INDENT>a = rankings[(b, n)]<EOL>a_partner, a_n = partners[a]<EOL>if a_partner == b:<EOL><INDENT>if is_paired:<EOL><INDENT>is_stable = False<EOL>partners[a] = (rankings[(a, a_n + <NUM_LIT:1>)], a_n + <NUM_LIT:1>)<EOL><DEDENT>else:<EOL><INDENT>is_paired = True<EOL><DEDENT><DEDENT><DEDENT><DEDENT><DEDENT>return sorted((a, b) for (a, (b, n)) in partners.items())<EOL>


ns = u'<STR_LIT>' % namespace<EOL>nsl = len(ns)<EOL>for elem in doc.getiterator():<EOL><INDENT>if elem.tag.startswith(ns):<EOL><INDENT>elem.tag = elem.tag[nsl:]<EOL>elem.attrib['<STR_LIT>'] = namespace<EOL><DEDENT><DEDENT>

Remove namespace in the passed document in place.


raise NotImplementedError()<EOL>


raise NotImplementedError()<EOL>

Returns an ElementTree document model of `xmldata`, which has been
declared to be of `contentType` format/encoding.


def __init__(self,<EOL>engine=None, storage=None, prefix='<STR_LIT>', owner=None,<EOL>autoCommit=None,<EOL>router=None, protocol=None, synchronizer=None, codec=None,<EOL>):

self.autoCommit = autoCommit if autoCommit is not None else engine is None<EOL>self._model = model.createModel(<EOL>engine     = engine,<EOL>storage    = storage,<EOL>prefix     = prefix,<EOL>owner_id   = owner,<EOL>context    = self,<EOL>)<EOL>self.router       = router<EOL>self.protocol     = protocol<EOL>self.synchronizer = synchronizer<EOL>self.codec        = codec<EOL>for attr in dir(self._model):<EOL><INDENT>if attr in ('<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>'):<EOL><INDENT>continue<EOL><DEDENT>value = getattr(self._model, attr)<EOL>if issubclass(value.__class__, sqlalchemy.ext.declarative.DeclarativeMeta)and value != self._model.DatabaseObject:<EOL><INDENT>setattr(self, attr, value)<EOL><DEDENT><DEDENT>

The Context constructor accepts the following parameters, of which
all are optional:

:param owner:

  an integer owner ID. Necessary primarily when the adapter
  storage is shared between multiple users/adapter agents
  (i.e. in server contexts). If it is not shared, `owner` can be
  left as ``None`` (the default).

:param storage:

  the sqlalchemy storage specification where all the SyncML-
  related data should be stored.

  NOTE: can be overridden by parameter `engine`.

  NOTE: the storage driver **MUST** support cascading deletes;
  this is done automatically for connections created directly by
  pysyncml for mySQL and sqlite, but it is up to the calling
  program to ensure this for other databases or if the database
  engine is passed in via parameter `engine`. Specifically, when
  pysyncml creates the sqlalchemy engine (i.e. by calling
  ``sqlalchemy.create_engine(storage)``), then InnoDB is requested
  for mySQL tables and ``PRAGMA foreign_keys=ON`` is issued for
  sqlite databases. pysyncml provides a helper function to ensure
  that sqlite databases have cascading deletes enabled::

    import sqlalchemy, pysyncml
    db = sqlalchemy.create_engine(...)
    pysyncml.enableSqliteCascadingDeletes(db)

:param engine:

  the sqlalchemy storage engine where all the SyncML-related
  data should be stored.

  NOTE: overrides parameter `storage`.

  NOTE: see notes under parameter `storage` for details on
  cascading delete support.

  TODO: it would be great to add a check to ensure that provided
  storage engines have cascading deletes enabled.

:param prefix:

  sets a database table name prefix. This is primarily useful when
  using the `engine` parameter, as multiple pysyncml contexts can
  then be defined within the same database namespace. Defaults to
  ``pysyncml``.

:param autoCommit:

  whether or not to execute a storage engine "commit" when syncing
  is complete. The default behavior is dependent on if `engine` is
  provided: if not ``None``, then `autoCommit` defaults to
  ``False``, otherwise, defaults to ``True``.

:param router:

  overrides the default router with an object that must implement
  the interface specified by :class:`pysyncml.router.Router`.

:param protocol:

  sets the semantic objective to/from protocol evaluation and
  resolution object, which must implement the
  :class:`pysyncml.protocol.Protocol` interface.

:param synchronizer:

  this is the engine for handling sync requests and dispatching
  them to the various agents. If specified, the object must
  implement the :class:`pysyncml.synchronizer.Synchronizer`
  interface.

:param codec:

  specify the codec used to encode the SyncML commands - typically
  either ``\'xml\'`` (the default) or ``\'wbxml\'``. It can also
  be an object that implements the :class:`pysyncml.codec.Codec`
  interface.


try:<EOL><INDENT>ret = self._model.Adapter.q(isLocal=True).one()<EOL>for k, v in kw.items():<EOL><INDENT>setattr(ret, k, v)<EOL><DEDENT><DEDENT>except NoResultFound:<EOL><INDENT>ret = self._model.Adapter(**kw)<EOL>ret.isLocal = True<EOL>self._model.session.add(ret)<EOL>if ret.devID is not None:<EOL><INDENT>self._model.session.flush()<EOL><DEDENT><DEDENT>ret.context       = self<EOL>ret.router        = self.router or router.Router(ret)<EOL>ret.protocol      = self.protocol or protocol.Protocol(ret)<EOL>ret.synchronizer  = self.synchronizer or synchronizer.Synchronizer(ret)<EOL>ret.codec         = self.codec or '<STR_LIT>'<EOL>if isinstance(ret.codec, basestring):<EOL><INDENT>ret.codec = codec.Codec.factory(ret.codec)<EOL><DEDENT>if ret.devID is not None:<EOL><INDENT>peers = ret.getKnownPeers()<EOL>if len(peers) == <NUM_LIT:1> and peers[<NUM_LIT:0>].url is not None:<EOL><INDENT>ret._peer = peers[<NUM_LIT:0>]<EOL><DEDENT><DEDENT>return ret<EOL>

.. TODO:: move this documentation into model/adapter.py?...

The Adapter constructor supports the following parameters:

:param devID:

  sets the local adapter\'s device identifier. For servers, this
  should be the externally accessible URL that launches the SyncML
  transaction, and for clients this should be a unique ID, such as
  the IMEI number (for mobile phones). If not specified, it will
  be defaulted to the `devID` of the `devinfo` object. If it
  cannot be loaded from the database or from the `devinfo`, then
  it must be provided before any synchronization can begin.

:param name:

  sets the local adapter\'s device name - usually a human-friendly
  description of this SyncML\'s function.

:param devinfo:

  sets the local adapter :class:`pysyncml.devinfo.DeviceInfo`.  If
  not specified, it will be auto-loaded from the database. If it
  cannot be loaded from the database, then it must be provided
  before any synchronization can begin.

:param peer:

  TODO: document...

:param maxGuidSize:

  TODO: document...

:param maxMsgSize:

  TODO: document...

:param maxObjSize:

  TODO: document...

:param conflictPolicy:

  sets the default conflict handling policy for this adapter,
  and can be overriden on a per-store basis (applies only when
  operating as the server role).


<EOL>ret = self._model.Adapter(isLocal=False, **kw)<EOL>self._model.session.add(ret)<EOL>if ret.devID is not None:<EOL><INDENT>self._model.session.flush()<EOL><DEDENT>return ret<EOL>

.. TODO:: move this documentation into model/adapter.py?...

The RemoteAdapter constructor supports the following parameters:

:param url:

  specifies the URL that this remote SyncML server can be reached
  at. The URL must be a fully-qualified URL.

:param auth:

  set what kind of authentication scheme to use, which generally is
  one of the following values:

    **None**:

      indicates no authentication is required.

    **pysyncml.NAMESPACE_AUTH_BASIC**:

      specifies to use "Basic-Auth" authentication scheme.

    **pysyncml.NAMESPACE_AUTH_MD5**:

      specifies to use MD5 "Digest-Auth" authentication scheme.
      NOTE: this may not be implemented yet...

:param username:

  if the `auth` is not ``None``, then the username to authenticate
  as must be provided via this parameter.

:param password:

  if the `auth` is not ``None``, then the password to authenticate
  with must be provided via this parameter.


for item in self.getAllItems():<EOL><INDENT>self.deleteItem(item.id)<EOL><DEDENT>

[OPTIONAL] Deletes all items stored by this Agent. The default
implementation simply iterates over :meth:`getAllItems` and
deletes them one at a time.


def dumpItem(self, item, stream, contentType=None, version=None):

raise NotImplementedError()<EOL>

Converts the specified `item` to serialized form (such that it can
transported over the wire) and writes it to the provided file-like
`stream` object. For agents that support multiple content-types,
the desired `contentType` and `version` will be specified as a
parameter. If `contentType` and `version` are None, appropriate
default values should be used. For agents that concurrently use
multiple content-types, the return value may be a two-element
tuple of (contentType, version), thus overriding or enhancing the
provided values.


def dumpsItem(self, item, contentType=None, version=None):

buf = six.StringIO()<EOL>ret = self.dumpItem(item, buf, contentType, version)<EOL>if ret is None:<EOL><INDENT>return buf.getvalue()<EOL><DEDENT>return (ret[<NUM_LIT:0>], ret[<NUM_LIT:1>], buf.getvalue())<EOL>

[OPTIONAL] Identical to :meth:`dump`, except the serialized form
is returned as a string representation. As documented in
:meth:`dump`, the return value can optionally be a three-element
tuple of (contentType, version, data) if the provided content-type
should be overridden or enhanced. The default implementation just
wraps :meth:`dump`.


def loadItem(self, stream, contentType=None, version=None):

raise NotImplementedError()<EOL>

Reverses the effects of the :meth:`dumpItem` method, and returns
the de-serialized Item from the file-like source `stream`.

Note: `version` will typically be ``None``, so it should either be
auto-determined, or not used. This is an issue in the SyncML
protocol, and is only here for symmetry with :meth:`dumpItem`
and as "future-proofing".


def loadsItem(self, data, contentType=None, version=None):

buf = six.StringIO(data)<EOL>return self.loadItem(buf, contentType, version)<EOL>

[OPTIONAL] Identical to :meth:`loadItem`, except the serialized
form is provided as a string representation in `data` instead of
as a stream. The default implementation just wraps
:meth:`loadItem`.


raise NotImplementedError()<EOL>

Returns an iterable of all the items stored in the local datastore.


raise NotImplementedError()<EOL>

The specified `item`, which will have been created via a prior
``loadItem()``, is added to the local datastore. This method
returns either a new :class:`pysyncml.Item
<pysyncml.items.base.Item>` instance or the same `item` that was
passed --- in either case, the returned item **MUST** have a valid
:attr:`pysyncml.Item.id <pysyncml.items.base.Item.id>` attribute.


raise NotImplementedError()<EOL>

Returns the :class:`pysyncml.Item <pysyncml.items.base.Item>`
instance associated with the specified `itemID`, which may or may
not have been converted to a string.


def replaceItem(self, item, reportChanges):

raise NotImplementedError()<EOL>

Updates the local datastore item with ID `item.id` to the value
provided as `item`, which will have been created via a prior
``loadItem()``.

If `reportChanges` is True, then the return value will be used to
track the changes that were applied. If `reportChanges` is True but
``None`` is returned, then change tracking will be disabled for
this change, which will cascade to any past or future changes that
have not yet been synchronized. The return value must be a string
(or an object that supports coercion via ``str()``). If multiple
changes accumulate for an object, they will be concatenated, in
order, and delimited via a semicolon (";"). See :doc:`../merging`
for details.


raise NotImplementedError()<EOL>

Deletes the local datastore item with ID `itemID`.


for match in self.getAllItems():<EOL><INDENT>if cmp(match, item) == <NUM_LIT:0>:<EOL><INDENT>return match<EOL><DEDENT><DEDENT>return None<EOL>

[OPTIONAL] Attempts to find the specified item and returns an item
that describes the same object although it's specific properties
may be different. For example, a contact whose name is an
identical match, but whose telephone number has changed would
return the matched item. ``None`` should be returned if no match
is found, otherwise the item that `item` matched should be
returned.

This is used primarily when a slow-sync is invoked and objects
that exist in both peers should not be replicated.

Note that **NO** merging of the items' properties should be done;
that will be initiated via a separate call to :meth:`mergeItems`.

This method by default will iterate over all items (by calling
:meth:`getAllItems`) and compare them using ``cmp()``. This means
that if the items managed by this agent implement the ``__eq__``
or ``__cmp__`` methods, then matching items will be detected and
returned. Otherwise, any items that exist in both peers will be
duplicated on slow-sync.

Sub-classes *should* implement a more efficient method of finding
matching items.

See :doc:`../merging` for details.


def mergeItems(self, localItem, remoteItem, changeSpec):

raise ConflictError('<STR_LIT>')<EOL>

[OPTIONAL] Merges the properties of `remoteItem`, which is an item
provided by a remote peer during a synchronization, into the
`localItem`, which is an item retrieved from this agent either via
:meth:`getItem` or :meth:`matchItem`. `changeSpec` will represent
the changes applied to `localItem` since `remoteItem` was last
synchronized, or will be ``None`` when called as a result of a
slow-sync :meth:`matchItem` call.

This method should return a new change-spec (see
:meth:`replaceItem` for details) that represents the changes
applied to `localItem` from `remoteItem`.

If the items cannot be merged, then a `pysyncml.ConflictError`
should be raised with more descriptive information on what failed
during the merge --- in which case pysyncml will revert to the
conflict resolution policy defined by `store.conflictPolicy` or
`adapter.conflictPolicy`.

IMPORTANT: if the merge fails, `localItem` and `remoteItem` must
stay untouched by this call; most importantly, if the merge fails
with a ConflictError, then `remoteItem` must be in the identical
state as when it entered the call.

This method by default raises a ConflictError, which means that if
any changes are made to the same item simultaneously by two
different peers, they will result in a conflict and will not be
auto-mergeable.

See :doc:`../merging` for details.


def pushChange(self, attribute, currentValue, newValue):

raise NotImplementedError()<EOL>

Record the change to the specified `attribute` from the original
value `currentValue` to `newValue`. The merger object itself
(i.e. ``self``) is returned, allowing multiple changes to be
chained. The change-spec returned by :meth:`getChangeSpec` will be
updated according to this merger's change detection strategy.

If `currentValue` is ``None``, the field is assumed to be *added*.
Conversely, if `newValue` is ``None``, the field is assumed to be
*deleted*. If both are ``None`` (or, more generally speaking,
equal), the request is ignored.


raise NotImplementedError()<EOL>

Returns the current change-spec representing all calls to
:meth:`pushChange` since construction of this merger.


def mergeChanges(self, attribute, localValue, remoteValue):

raise NotImplementedError()<EOL>

Returns the value of the specified `attribute` as determined by
the change-spec stored by this, the current `localValue` of this
SyncML peer (i.e. the serve-side) and the client-provided
`remoteValue` (i.e. of the remote client-side). Raises a
`pysyncml.ConflictError` if the local changes conflict with the
value provided by the remote peer.

If `localValue` is ``None``, the field is assumed to not exist
locally. Conversely, if `remoteValue` is ``None``, the field is
assumed to not exist on the remote peer. If both are ``None`` (or,
more generally speaking, equal), the value is returned as-is
without further investigation.


raise NotImplementedError()<EOL>

Returns a :class:`Merger` for the specified `changeSpec` which can
be ``None`` if the merger is intended to generate a change-spec.


def __init__(self, sharedDefault=True, default=None, mergers=None, **kw):

self.shared  = sharedDefault<EOL>self.default = default or AttributeMergerFactory()<EOL>self.mergers = mergers or dict()<EOL>self.mergers.update(kw)<EOL>

The CompositeMergerFactory constructor accepts the following
parameters:

:param default:

  The default merger factory (if unspecified, defaults to an
  AttributeMergerFactory). See `sharedDefault` if the default
  is not an attribute-based merger factory.

:param mergers:

  A dictionary of (attribute => MergerFactory) that override the
  default merger factory for the specified attribute. If
  unspecified, all attributes will use the default merger factory.

:param sharedDefault:

  The `sharedDefault` parameter controls how default attributes
  get handled. When ``True`` (the default), then all default
  attributes will share a Merger and the Merger will be passed the
  attribute name during operations. When ``False``, then each
  attribute will get its own Merger and operations will not get
  the attribute name. It is important that the `default` and
  `sharedDefault` parameters match - for example, if `default` is
  set to a ``TextMergerFactory``, then `sharedDefault` must be set
  to ``False``.


def append(self, fieldname, changeType, initialValue=None, isMd5=False):

raise NotImplementedError()<EOL>

Add a change to this ChangeTracker.

:param fieldname:

  The item attribute that was changed in some way. The type of
  `fieldname` is dependent on which subclass of ChangeTracker is
  being used.

:param changeType:

  The type of change that was applied to `fieldname`, which can be
  one of ``pysyncml.ITEM_ADDED``, ``pysyncml.ITEM_MODIFIED``, or
  ``pysyncml.ITEM_DELETED``.

:param initialValue:

  For non-ADDED change types, specifies the *initial* value of the
  field, before the change was applied. Note that if the
  `initialValue` is very large, an MD5 checksum can be provided
  instead, in which case `isMd5` should be set to ``True``.

:param isMd5:

  Specifies whether `initialValue` is an MD5 checksum or not. For
  large values of `initialValue` the ChangeTrackers will
  automatically convert it to a checksum, but this allows the
  caller to potentially do some additional optimizations.


def isChange(self, fieldname, changeType, newValue=None, isMd5=False):

raise NotImplementedError()<EOL>

Checks to see if the specified field should be changed to the
`newValue`, first checking to see if the change conflicts with the
change-spec stored by this ChangeTracker. IMPORTANT: the
`changeType` is relative to the **current** local value, as
recorded by the changes stored by this tracker from the
**initial** value.

See :meth:`update` for a layer above.

This method will terminate in one of the following three ways:

* returns None:

  The `newValue` is actually outdated, but does not conflict.
  The field should be left as-is.

* returns `changeObject`:

  If any form of change should be applied as a result of this
  change request, then `changeObject` will be non-None and will
  define how. The exact nature of the object is ChangeTracker
  subclass dependent.

* raises `pysyncml.ConflictError`:

  The `newValue` conflicts with a change made by another source
  and should be handled by following conflict resolution policy.

For example, if two clients and a server are tracking changes
made to the following fields::

  initial values (on server, client 1 and client 2):
    field "a" => "A"    (will not change)
    field "b" => "B"    (will be modified by client 1)
    field "c" => "C"    (will be deleted by client 1)
    field "d" => "D"    (will be modified by client 2)
    field "e" => "E"    (will be a conflict)
    field "f" => "F"    (will be modified identically)

  client 1 changes:
    does not alter field "a"
    modifies field "b" to "Bmod"
    deletes field "c"
    does not alter field "d"
    deletes field "e"
    modifies field "f" to "Fmod"

  client 2 changes (simultaneous to client 1 changes):
    does not alter field "a"
    does not alter field "b"
    does not alter field "c"
    modifies field "d" to "Dmod"
    modifies field "e" to "Emod"
    modifies field "f" to "Fmod"

  client 1 synchronizes with server ==> server values:
    field "b" => "Bmod"
    deletes fields "c" and "e"
    field "f" => "Fmod"
    change-spec for client 2: "mod:b@vB,f@vF|del:c@vC,e@vE"

  when client 2 synchronizes, the framework detects a conflict and
  requests a merge attempt by the agent. the agent then compares the
  current values and those presented by client 2 and determines:
    - field "a" is unchanged
    - field "b" differs: changed to "B"
    - field "c" differs: added as "C"
    - field "d" differs: change to "Dmod"
    - field "e" differs: added as "Dmod"
    - field "f" is unchanged

  for the fields that are mismatches (i.e. fields "b", "c", "d",
  and "e"), the agent checks with this change tracker ("ct") to
  see if it was actually a change, and if so, if it conflicts:

    - ct.isChange('b', 'B')    ==> None
    - ct.isChange('c', 'C')    ==> None
    - ct.isChange('d', 'Dmod') ==> 'd'
    - ct.isChange('e', 'Emod') ==> raises ConflictError

Note that this assumes that the caller will have verified that the
remote `currentValue` is **not** equal to the local active value -
i.e. that there is some difference between the `fieldname` values,
and a resolution needs to be negotiated.

:param newValue:

  A string representing the value that is being tested for
  conflicts or outdated-ness.

.. TODO:: perhaps rename this method?...


def update(self, fieldname, localValue, remoteValue):

if localValue == remoteValue:<EOL><INDENT>return localValue<EOL><DEDENT>ct = constants.ITEM_DELETED if remoteValue is None else constants.ITEM_MODIFIED<EOL>if localValue is None:<EOL><INDENT>ct = constants.ITEM_ADDED<EOL><DEDENT>changed = self.isChange(fieldname, ct, remoteValue)<EOL>if changed is None:<EOL><INDENT>return localValue<EOL><DEDENT>self.append(changed, ct, initialValue=localValue, isMd5=False)<EOL>return remoteValue<EOL>

Returns the appropriate current value, based on the changes
recorded by this ChangeTracker, the value stored by the server
(`localValue`), and the value stored by the synchronizing client
(`remoteValue`). If `remoteValue` conflicts with changes stored
locally, then a `pysyncml.ConflictError` is raised.

If a change needs to be applied because `remoteValue` has been
updated, then the new value will be returned, and this
ChangeTracker will be updated such that a call to
:meth:`getChangeSpec` will incorporate the change.

:param fieldname:

  The name of the fieldname being evaluated.

:param localValue:

  The value of the field as stored by the server, usually the one that
  also stored the current change-spec. If `localValue` is ``None``,
  then it is assumed that the field was potentially added (this will
  first be verified against the stored change-spec).

:param remoteValue:

  The new value being presented that may or may not be a source of
  conflict. If `remoteValue` is ``None``, then it is assumed that
  the field was potentially deleted (this will first be verified
  against the stored change-spec).


return self._changes2spec(self.allchanges)<EOL>

Returns a string-representation of *all* changes recorded by this
ChangeTracker, including those provided in the constructor and any
calls to `pushChangeSpec()`. Note that this is usually *NOT* what
you are looking for when reporting changes to the pysyncml
framework -- for that, see :meth:`getChangeSpec`.


return self._changes2spec(self.changes)<EOL>

Returns a string-representation of the changes recorded by this
ChangeTracker that were reported since construction (or calls to
pushChangeSpec()) by calls to :meth:`append` or :meth:`update`.

This is similar to, but distinct from, :meth:`getFullChangeSpec`.


def __init__(self, changeSpec=None, *args, **kw):

self.baseline = dict()<EOL>self.current  = dict()<EOL>super(AttributeChangeTracker, self).__init__(changeSpec, *args, **kw)<EOL>

Initializes this AttributeChangeTracker with the provided
`changeSpec`, which is expected to be in the same format as what
would have been returned by a call to ``str()`` on this
object. The change-spec will look similar to::

  add:tel-home|mod:firstname@m68b329d...,lastname@mh4d9...|del:tel-pager@mba45...

If `changeSpec` is not specified, this AttributeChangeTracker will
start assuming no prior changes were made to any fields.


<EOL>changes = self._collapseChanges(self.baseline, self.current)<EOL>if fieldname not in changes:<EOL><INDENT>return fieldname<EOL><DEDENT>cur = changes[fieldname]<EOL>if changeType == constants.ITEM_DELETED:<EOL><INDENT>if cur.op == constants.ITEM_ADDED or cur.op == constants.ITEM_DELETED:<EOL><INDENT>return None<EOL><DEDENT>raise ConflictError('<STR_LIT>'<EOL>% (fieldname,))<EOL><DEDENT>if isMd5Equal(newValue, isMd5, cur.ival, cur.md5):<EOL><INDENT>return None<EOL><DEDENT>raise ConflictError(<EOL>'<STR_LIT>' % (fieldname,))<EOL>

Implements as specified in :meth:`.ChangeTracker.isChange` where
the `changeObject` is simply the fieldname that needs to be
updated with the `newValue`. Currently, this is always equal to
`fieldname`.


self.baseline = []<EOL>self.current  = []<EOL>super(ListChangeTracker, self).__init__(changeSpec, *args, **kw)<EOL>

Initializes this ListChangeTracker with the provided `changeSpec`,
which is expected to be in the same format as what would have been
returned by a call to ``str()`` on this object. The change-spec
will look similar to::

  2:a,1:M68b329d...,1:mh4d9,2:Dba45...,3:a

If `changeSpec` is not specified, this ListChangeTracker will
start assuming no prior changes were made to any content and will
expect changes to be reported via :meth:`pushChange`.


def append(self, listIndex, changeType, initialValue=None, isMd5=False):

if not isMd5 and initialValue is not None and len(initialValue) > <NUM_LIT:32>:<EOL><INDENT>initialValue = hashlib.md5(initialValue).hexdigest()<EOL>isMd5        = True<EOL><DEDENT>cur = adict(index = int(listIndex),<EOL>op    = changeType,<EOL>ival  = initialValue,<EOL>md5   = isMd5)<EOL>for idx, val in enumerate(self.current):<EOL><INDENT>if val.index < cur.index:<EOL><INDENT>continue<EOL><DEDENT>if val.index > cur.index:<EOL><INDENT>self.current.insert(idx, cur)<EOL>break<EOL><DEDENT>raise InvalidChangeSpec('<STR_LIT>' % (cur.index,))<EOL><DEDENT>else:<EOL><INDENT>self.current.append(cur)<EOL><DEDENT>

Adds a change spec to the current list of changes. The `listIndex`
represents the line number (in multi-line mode) or word number (in
single-line mode), and must be **INCLUSIVE** of both additions and
deletions.


def isChange(self, listIndex, changeType, newValue=None, isMd5=False, token=None):

<EOL>adjust  = <NUM_LIT:0>               <EOL>token   = token           <EOL>index   = int(listIndex)<EOL>ret     = index<EOL>changes = self._collapseChanges(self.baseline, self.current)<EOL>for cur in changes:<EOL><INDENT>if cur.index > index:<EOL><INDENT>if changeType != constants.ITEM_ADDED:<EOL><INDENT>return (ret, None)<EOL><DEDENT>if token is None or token[<NUM_LIT:0>] != index - adjust:<EOL><INDENT>token = (ret, <NUM_LIT:0>)<EOL><DEDENT>token = (ret, token[<NUM_LIT:1>] + <NUM_LIT:1>)<EOL>return (ret, token)<EOL><DEDENT>if cur.index != index:<EOL><INDENT>if cur.op == constants.ITEM_DELETED:<EOL><INDENT>index  += <NUM_LIT:1><EOL>adjust += <NUM_LIT:1><EOL><DEDENT>continue<EOL><DEDENT>if token is not None and token[<NUM_LIT:0>] == index - adjust:<EOL><INDENT>index += token[<NUM_LIT:1>]<EOL>continue<EOL><DEDENT>if changeType == constants.ITEM_DELETED:<EOL><INDENT>if cur.op == constants.ITEM_ADDED:<EOL><INDENT>return (None, None)<EOL><DEDENT>raise ConflictError(<EOL>'<STR_LIT>' % (index,))<EOL><DEDENT>if changeType == constants.ITEM_ADDED:<EOL><INDENT>if token is None:<EOL><INDENT>token = (ret, <NUM_LIT:0>)<EOL><DEDENT>token = (ret, token[<NUM_LIT:1>] + <NUM_LIT:1>)<EOL>if cur.op == constants.ITEM_DELETED:<EOL><INDENT>if isMd5Equal(newValue, isMd5, cur.ival, cur.md5):<EOL><INDENT>return (None, token)<EOL><DEDENT><DEDENT>return (ret, token)<EOL><DEDENT>if cur.op == constants.ITEM_DELETED:<EOL><INDENT>index  += <NUM_LIT:1><EOL>adjust += <NUM_LIT:1><EOL>continue<EOL><DEDENT>if cur.op == constants.ITEM_ADDED:<EOL><INDENT>raise ConflictError(<EOL>'<STR_LIT>' % (index,))<EOL><DEDENT>if isMd5Equal(newValue, isMd5, cur.ival, cur.md5):<EOL><INDENT>return (None, None)<EOL><DEDENT>raise ConflictError(<EOL>'<STR_LIT>' % (index,))<EOL><DEDENT>if changeType != constants.ITEM_ADDED:<EOL><INDENT>return (ret, None)<EOL><DEDENT>if token is None or token[<NUM_LIT:0>] != index - adjust:<EOL><INDENT>token = (ret, <NUM_LIT:0>)<EOL><DEDENT>token = (ret, token[<NUM_LIT:1>] + <NUM_LIT:1>)<EOL>return (ret, token)<EOL>

Implements as specified in :meth:`.ChangeTracker.isChange` where
the `changeObject` is a two-element tuple. The first element is
the index at which the change should be applied, and the second
element is an abstract token that should be passed back into this
method at every iteration.

IMPORTANT: unlike the AttributeChangeTracker, the
ListChangeTracker's `isChange()` method is sensitive to order
(which is why it uses the `changeObject` and `token`
mechanisms. Therefore, it is important to call `isChange()`
sequentially with all changes in the order that they occur in the
change list.


ret = difflib.get_close_matches(base.uri, [ds1.uri, ds2.uri], <NUM_LIT:1>, cutoff=<NUM_LIT:0.5>)<EOL>if len(ret) <= <NUM_LIT:0>:<EOL><INDENT>return <NUM_LIT:0><EOL><DEDENT>if ret[<NUM_LIT:0>] == ds1.uri:<EOL><INDENT>return -<NUM_LIT:1><EOL><DEDENT>return <NUM_LIT:1><EOL>

Bases the comparison of the datastores on URI alone.


return int(platform.architecture(bits='<STR_LIT>')[<NUM_LIT:0>].replace('<STR_LIT>', '<STR_LIT>'))<EOL>

Returns the size of a memory address reference on the current
    platform (e.g. 32 or 64 for respectively 32-bit or 64-bit operating
    platforms) - defaults to 32 if it cannot be determined.


return min(sys.maxint, int(pow(<NUM_LIT:2>,<NUM_LIT>)-<NUM_LIT:1>))<EOL>

Returns the maximum size of a memory object. By default this is,
    set to ``sys.maxint``, however the `context` may override this behavior.

    NOTE: currently, this is being hardcoded to a maximum of 2GB for
          compatibility with funambol servers, which croak above that
          value.

    TODO: allow the context to control this, or implement auto-detect to
          determine what the remote peer can support...


def describeStats(stats, stream, title=None, details=True, totals=True, gettext=None):

from . import state<EOL>modeStringLut = dict((<EOL>(constants.SYNCTYPE_TWO_WAY,             '<STR_LIT>'),<EOL>(constants.SYNCTYPE_SLOW_SYNC,           '<STR_LIT>'),<EOL>(constants.SYNCTYPE_ONE_WAY_FROM_CLIENT, '<STR_LIT>'),<EOL>(constants.SYNCTYPE_REFRESH_FROM_CLIENT, '<STR_LIT>'),<EOL>(constants.SYNCTYPE_ONE_WAY_FROM_SERVER, '<STR_LIT>'),<EOL>(constants.SYNCTYPE_REFRESH_FROM_SERVER, '<STR_LIT>'),<EOL>))<EOL>if gettext is not None:<EOL><INDENT>_ = gettext<EOL><DEDENT>else:<EOL><INDENT>_ = lambda s: s<EOL><DEDENT>wSrc  = len(_('<STR_LIT>'))<EOL>wMode = len(_('<STR_LIT>'))<EOL>wCon  = len(_('<STR_LIT>'))<EOL>wCol  = len(_('<STR_LIT>'))<EOL>wMrg  = len(_('<STR_LIT>'))<EOL>wHereAdd = wPeerAdd = len(_('<STR_LIT>'))<EOL>wHereMod = wPeerMod = len(_('<STR_LIT>'))<EOL>wHereDel = wPeerDel = len(_('<STR_LIT>'))<EOL>wHereErr = wPeerErr = len(_('<STR_LIT>'))<EOL>totLoc = <NUM_LIT:0><EOL>totRem = <NUM_LIT:0><EOL>totErr = <NUM_LIT:0><EOL>totCol = <NUM_LIT:0><EOL>totMrg = <NUM_LIT:0><EOL>for key in stats.keys():<EOL><INDENT>wSrc  = max(wSrc, len(key))<EOL>wMode = max(wMode, len(modeStringLut.get(stats[key].mode)))<EOL>wCol  = max(wCol, len(num2str(stats[key].conflicts)))<EOL>wMrg  = max(wMrg, len(num2str(stats[key].merged)))<EOL>wHereAdd = max(wHereAdd, len(num2str(stats[key].hereAdd)))<EOL>wPeerAdd = max(wPeerAdd, len(num2str(stats[key].peerAdd)))<EOL>wHereMod = max(wHereMod, len(num2str(stats[key].hereMod)))<EOL>wPeerMod = max(wPeerMod, len(num2str(stats[key].peerMod)))<EOL>wHereDel = max(wHereDel, len(num2str(stats[key].hereDel)))<EOL>wPeerDel = max(wPeerDel, len(num2str(stats[key].peerDel)))<EOL>wHereErr = max(wHereErr, len(num2str(stats[key].hereErr)))<EOL>wPeerErr = max(wPeerErr, len(num2str(stats[key].peerErr)))<EOL>totLoc += stats[key].hereAdd + stats[key].hereMod + stats[key].hereDel<EOL>totRem += stats[key].peerAdd + stats[key].peerMod + stats[key].peerDel<EOL>totErr += stats[key].hereErr + stats[key].peerErr<EOL>totCol += stats[key].conflicts<EOL>totMrg += stats[key].merged<EOL><DEDENT>if wCon > wCol + <NUM_LIT:3> + wMrg:<EOL><INDENT>diff = wCon - ( wCol + <NUM_LIT:3> + wMrg )<EOL>wCol += diff / <NUM_LIT:2><EOL>wMrg = wCon - <NUM_LIT:3> - wCol<EOL><DEDENT>else:<EOL><INDENT>wCon = wCol + <NUM_LIT:3> + wMrg<EOL><DEDENT>if details:<EOL><INDENT>tWid = ( wSrc + <NUM_LIT:3> + wMode + <NUM_LIT:3><EOL>+ wHereAdd + wHereMod + wHereDel + wHereErr + <NUM_LIT:9> + <NUM_LIT:3><EOL>+ wPeerAdd + wPeerMod + wPeerDel + wPeerErr + <NUM_LIT:9> + <NUM_LIT:3><EOL>+ wCon )<EOL><DEDENT>else:<EOL><INDENT>if title is None:<EOL><INDENT>tWid = <NUM_LIT:0><EOL><DEDENT>else:<EOL><INDENT>tWid = len(title)<EOL><DEDENT><DEDENT>if totals:<EOL><INDENT>sumlist = []<EOL>for val, singular, plural in [<EOL>(totLoc, _('<STR_LIT>'), _('<STR_LIT>')),<EOL>(totRem, _('<STR_LIT>'), _('<STR_LIT>')),<EOL>(totErr, _('<STR_LIT:error>'), _('<STR_LIT>')),<EOL>]:<EOL><INDENT>if val == <NUM_LIT:1>:<EOL><INDENT>sumlist.append(num2str(val) + '<STR_LIT:U+0020>' + singular)<EOL><DEDENT>elif val > <NUM_LIT:1>:<EOL><INDENT>sumlist.append(num2str(val) + '<STR_LIT:U+0020>' + plural)<EOL><DEDENT><DEDENT>if len(sumlist) <= <NUM_LIT:0>:<EOL><INDENT>sumlist = _('<STR_LIT>')<EOL><DEDENT>elif len(sumlist) == <NUM_LIT:1>:<EOL><INDENT>sumlist = sumlist[<NUM_LIT:0>]<EOL><DEDENT>else:<EOL><INDENT>sumlist = '<STR_LIT:U+002CU+0020>'.join(sumlist[:-<NUM_LIT:1>]) + '<STR_LIT:U+0020>' + _('<STR_LIT>') + '<STR_LIT:U+0020>' + sumlist[-<NUM_LIT:1>]<EOL><DEDENT>if totMrg > <NUM_LIT:0> or totCol > <NUM_LIT:0>:<EOL><INDENT>sumlist += '<STR_LIT>'<EOL>if totMrg == <NUM_LIT:1>:<EOL><INDENT>sumlist += num2str(totMrg) + '<STR_LIT:U+0020>' + _('<STR_LIT>')<EOL><DEDENT>elif totMrg > <NUM_LIT:1>:<EOL><INDENT>sumlist += num2str(totMrg) + '<STR_LIT:U+0020>' + _('<STR_LIT>')<EOL><DEDENT>if totMrg > <NUM_LIT:0> and totCol > <NUM_LIT:0>:<EOL><INDENT>sumlist += '<STR_LIT:U+0020>' + _('<STR_LIT>') + '<STR_LIT:U+0020>'<EOL><DEDENT>if totCol == <NUM_LIT:1>:<EOL><INDENT>sumlist += num2str(totCol) + '<STR_LIT:U+0020>' + _('<STR_LIT>')<EOL><DEDENT>elif totCol > <NUM_LIT:1>:<EOL><INDENT>sumlist += num2str(totCol) + '<STR_LIT:U+0020>' + _('<STR_LIT>')<EOL><DEDENT><DEDENT>sumlist += '<STR_LIT:.>'<EOL>if len(sumlist) > tWid:<EOL><INDENT>wSrc += len(sumlist) - tWid<EOL>tWid = len(sumlist)<EOL><DEDENT><DEDENT>if title is not None:<EOL><INDENT>stream.write('<STR_LIT>' + '<STR_LIT:->' * tWid + '<STR_LIT>')<EOL>stream.write('<STR_LIT>'.format(title, w=tWid))<EOL>stream.write('<STR_LIT>')<EOL><DEDENT>hline = '<STR_LIT>'+ '<STR_LIT:->' * wSrc+ '<STR_LIT>'+ '<STR_LIT:->' * wMode+ '<STR_LIT>'+ '<STR_LIT:->' * ( wHereAdd + wHereMod + wHereDel + wHereErr + <NUM_LIT:9> )+ '<STR_LIT>'+ '<STR_LIT:->' * ( wPeerAdd + wPeerMod + wPeerDel + wPeerErr + <NUM_LIT:9> )+ '<STR_LIT>'+ '<STR_LIT:->' * wCon+ '<STR_LIT>'<EOL>if details:<EOL><INDENT>stream.write(hline)<EOL>stream.write('<STR_LIT>' + '<STR_LIT:U+0020>' * wSrc)<EOL>stream.write('<STR_LIT>' + '<STR_LIT:U+0020>' * wMode)<EOL>stream.write('<STR_LIT>'.format(_('<STR_LIT>'), w=( wHereAdd + wHereMod + wHereDel + wHereErr + <NUM_LIT:9> )))<EOL>stream.write('<STR_LIT>'.format(_('<STR_LIT>'), w=( wPeerAdd + wPeerMod + wPeerDel + wPeerErr + <NUM_LIT:9> )))<EOL>stream.write('<STR_LIT>'.format(_('<STR_LIT>'), w=wCon))<EOL>stream.write('<STR_LIT>')<EOL>stream.write('<STR_LIT>'.format(_('<STR_LIT>'), w=wSrc))<EOL>stream.write('<STR_LIT>'.format(_('<STR_LIT>'), w=wMode))<EOL>stream.write('<STR_LIT>'.format(_('<STR_LIT>'), w=wHereAdd))<EOL>stream.write('<STR_LIT>'.format(_('<STR_LIT>'), w=wHereMod))<EOL>stream.write('<STR_LIT>'.format(_('<STR_LIT>'), w=wHereDel))<EOL>stream.write('<STR_LIT>'.format(_('<STR_LIT>'), w=wHereErr))<EOL>stream.write('<STR_LIT>'.format(_('<STR_LIT>'), w=wPeerAdd))<EOL>stream.write('<STR_LIT>'.format(_('<STR_LIT>'), w=wPeerMod))<EOL>stream.write('<STR_LIT>'.format(_('<STR_LIT>'), w=wPeerDel))<EOL>stream.write('<STR_LIT>'.format(_('<STR_LIT>'), w=wPeerErr))<EOL>stream.write('<STR_LIT>'.format(_('<STR_LIT>'), w=wCol))<EOL>stream.write('<STR_LIT>'.format(_('<STR_LIT>'), w=wMrg))<EOL>stream.write('<STR_LIT>')<EOL>hsline = '<STR_LIT>' + '<STR_LIT:->' * wSrc+ '<STR_LIT>' + '<STR_LIT:->' * wMode+ '<STR_LIT>' + '<STR_LIT:->' * wHereAdd+ '<STR_LIT>' + '<STR_LIT:->' * wHereMod+ '<STR_LIT>' + '<STR_LIT:->' * wHereDel+ '<STR_LIT>' + '<STR_LIT:->' * wHereErr+ '<STR_LIT>' + '<STR_LIT:->' * wPeerAdd+ '<STR_LIT>' + '<STR_LIT:->' * wPeerMod+ '<STR_LIT>' + '<STR_LIT:->' * wPeerDel+ '<STR_LIT>' + '<STR_LIT:->' * wPeerErr+ '<STR_LIT>' + '<STR_LIT:->' * wCol+ '<STR_LIT>' + '<STR_LIT:->' * wMrg+ '<STR_LIT>'<EOL>stream.write(hsline)<EOL>def numcol(val, wid):<EOL><INDENT>if val == <NUM_LIT:0>:<EOL><INDENT>return '<STR_LIT>'.format('<STR_LIT:->', w=wid)<EOL><DEDENT>return '<STR_LIT>'.format(num2str(val), w=wid)<EOL><DEDENT>for key in sorted(stats.keys(), key=lambda k: str(k).lower()):<EOL><INDENT>stream.write('<STR_LIT>'.format(key, w=wSrc))<EOL>stream.write('<STR_LIT>'.format(modeStringLut.get(stats[key].mode), w=wMode))<EOL>stream.write(numcol(stats[key].hereAdd, wHereAdd))<EOL>stream.write(numcol(stats[key].hereMod, wHereMod))<EOL>stream.write(numcol(stats[key].hereDel, wHereDel))<EOL>stream.write(numcol(stats[key].hereErr, wHereErr))<EOL>stream.write(numcol(stats[key].peerAdd, wPeerAdd))<EOL>stream.write(numcol(stats[key].peerMod, wPeerMod))<EOL>stream.write(numcol(stats[key].peerDel, wPeerDel))<EOL>stream.write(numcol(stats[key].peerErr, wPeerErr))<EOL>stream.write(numcol(stats[key].conflicts, wCol))<EOL>stream.write(numcol(stats[key].merged, wMrg))<EOL>stream.write('<STR_LIT>')<EOL><DEDENT>stream.write(hsline)<EOL><DEDENT>if totals:<EOL><INDENT>if title is None and not details:<EOL><INDENT>stream.write('<STR_LIT>' + '<STR_LIT:->' * tWid + '<STR_LIT>')<EOL><DEDENT>stream.write('<STR_LIT>'.format(sumlist, w=tWid))<EOL>stream.write('<STR_LIT>')<EOL>stream.write('<STR_LIT>' + '<STR_LIT:->' * tWid + '<STR_LIT>')<EOL><DEDENT>return<EOL>

Renders an ASCII-table of the synchronization statistics `stats`,
example output:

.. code-block::

  +----------------------------------------------------------------------------------+
  |                                      TITLE                                       |
  +----------+------+-------------------------+--------------------------+-----------+
  |          |      |          Local          |          Remote          | Conflicts |
  |   Source | Mode |  Add  | Mod | Del | Err |   Add  | Mod | Del | Err | Col | Mrg |
  +----------+------+-------+-----+-----+-----+--------+-----+-----+-----+-----+-----+
  | contacts |  <=  |   -   |  -  |  -  |  -  | 10,387 |  -  |  -  |  -  |  -  |  -  |
  |     note |  SS  | 1,308 |  -  |   2 |  -  |    -   |  -  |  -  |  -  |  -  |  -  |
  +----------+------+-------+-----+-----+-----+--------+-----+-----+-----+-----+-----+
  |                  1,310 local changes and 10,387 remote changes.                  |
  +----------------------------------------------------------------------------------+

:Parameters:

stats : dict

  The synchronization stats returned by a call to Adapter.sync().

stream : file-like-object

  An output file-like object that has at least a `write()` method,
  e.g. ``sys.stdout`` can be used.

title : str, optional, default: null

  A title placed at the top of the table -- if omitted (the default),
  then no title is rendered.

details : bool, optional, default: true

  If truthy, a per-datastore listing of changes will be displayed
  (as in the above example).

totals : bool, optional, default: true

  If truthy, a summary of all changes will be displayed (as in the
  above example).

gettext : callable, optional, @DEPRECATED(0.2.0), default: null

  A `gettext.gettext` compatible callable used for translating
  localized content (such as number formatting, etc.).

  NOTE: this parameter is deprecated, and will be replaced with
  a generalized i18n solution.


def dump(self, stream, contentType=None, version=None):

raise NotImplementedError()<EOL>

Converts this Item to serialized form (such that it can be
transported over the wire) and writes it to the provided file-like
`stream` object. For agents that support multiple content-types,
the desired `contentType` and `version` will be specified as a
parameter. If `contentType` and `version` are None, appropriate
default values should be used. For agents that concurrently use
multiple content-types, the return value may be a two-element
tuple of (contentType, version), thus overriding or enhancing the
provided values.


def dumps(self, contentType=None, version=None):

buf = six.StringIO()<EOL>ret = self.dump(buf, contentType, version)<EOL>if ret is None:<EOL><INDENT>return buf.getvalue()<EOL><DEDENT>return (ret[<NUM_LIT:0>], ret[<NUM_LIT:1>], buf.getvalue())<EOL>

[OPTIONAL] Identical to :meth:`dump`, except the serialized form
is returned as a string representation. As documented in
:meth:`dump`, the return value can optionally be a three-element
tuple of (contentType, version, data) if the provided content-type
should be overridden or enhanced. The default implementation just
wraps :meth:`dump`.


@classmethod<EOL><INDENT>def load(cls, stream, contentType=None, version=None):<DEDENT>

raise NotImplementedError()<EOL>

Reverses the effects of the :meth:`dump` method, and returns the
de-serialized Item from the file-like source `stream`.

Note: `version` will typically be ``None``, so it should either be
auto-determined, or not used. This is an issue in the SyncML
protocol, and is only here for symmetry with :meth:`dump` and as
"future-proofing".


@classmethod<EOL><INDENT>def loads(cls, data, contentType=None, version=None):<DEDENT>

buf = six.StringIO(data)<EOL>return cls.load(buf, contentType, version)<EOL>

[OPTIONAL] Identical to :meth:`load`, except the serialized form
is provided as a string representation in `data` instead of as a
stream. The default implementation just wraps :meth:`load`.