Package node_manager_fkie :: Module sync_dialog
[frames] | no frames]

Source Code for Module node_manager_fkie.sync_dialog

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2012, Fraunhofer FKIE/US, Alexander Tiderko 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Fraunhofer nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32   
 33  from python_qt_binding.QtCore import QObject, QRegExp, Qt, Signal 
 34  from python_qt_binding.QtGui import QFont, QIcon, QSyntaxHighlighter, QTextCharFormat 
 35  import os 
 36  import threading 
 37   
 38  import rospy 
 39   
 40  from node_manager_fkie.common import is_package 
 41  from node_manager_fkie.detailed_msg_box import WarningMessageBox 
 42  import node_manager_fkie as nm 
 43   
 44  from .editor import TextEdit 
 45  try: 
 46      from python_qt_binding.QtGui import QApplication, QMessageBox, QVBoxLayout, QSizePolicy 
 47      from python_qt_binding.QtGui import QComboBox, QDialog, QDialogButtonBox, QFileDialog, QToolButton 
 48  except: 
 49      from python_qt_binding.QtWidgets import QApplication, QMessageBox, QVBoxLayout, QSizePolicy 
 50      from python_qt_binding.QtWidgets import QComboBox, QDialog, QDialogButtonBox, QFileDialog, QToolButton 
51 52 53 -class SyncHighlighter(QSyntaxHighlighter):
54 ''' 55 Enabled the syntax highlightning for the sync interface. 56 ''' 57
58 - def __init__(self, parent=None):
59 QSyntaxHighlighter.__init__(self, parent) 60 self.rules = [] 61 self.commentStart = QRegExp("#") 62 self.commentEnd = QRegExp("\n") 63 self.commentFormat = QTextCharFormat() 64 self.commentFormat.setFontItalic(True) 65 self.commentFormat.setForeground(Qt.darkGray) 66 f = QTextCharFormat() 67 r = QRegExp() 68 r.setMinimal(True) 69 f.setFontWeight(QFont.Normal) 70 f.setForeground(Qt.darkBlue) 71 tagList = ["\\bignore_hosts\\b", "\\bsync_hosts\\b", 72 "\\bignore_nodes\\b", "\\bsync_nodes\\b", 73 "\\bignore_topics\\b", "\\bignore_publishers\\b", 74 "\\bignore_topics\\b", "\\bsync_topics\\b", 75 "\\bignore_subscribers\\b", "\\bsync_services\\b", 76 "\\bsync_topics_on_demand\\b", "\\bsync_remote_nodes\\b"] 77 for tag in tagList: 78 r.setPattern(tag) 79 self.rules.append((QRegExp(r), QTextCharFormat(f))) 80 81 f.setForeground(Qt.darkGreen) 82 f.setFontWeight(QFont.Bold) 83 attrList = ["\\b\\*|\\*\\B|\\/\\*"] 84 for attr in attrList: 85 r.setPattern(attr) 86 self.rules.append((QRegExp(r), QTextCharFormat(f)))
87 88 # f.setForeground(Qt.red) 89 # f.setFontWeight(QFont.Bold) 90 # attrList = ["\\s\\*"] 91 # for attr in attrList: 92 # r.setPattern(attr) 93 # self.rules.append((QRegExp(r), QTextCharFormat(f))) 94
95 - def highlightBlock(self, text):
96 for pattern, myformat in self.rules: 97 index = pattern.indexIn(text) 98 while index >= 0: 99 length = pattern.matchedLength() 100 self.setFormat(index, length, myformat) 101 index = pattern.indexIn(text, index + length) 102 103 self.setCurrentBlockState(0) 104 startIndex = 0 105 if self.previousBlockState() != 1: 106 startIndex = self.commentStart.indexIn(text) 107 if startIndex >= 0: 108 commentLength = len(text) - startIndex 109 self.setFormat(startIndex, commentLength, self.commentFormat)
110
111 112 -class SyncDialog(QDialog):
113 ''' 114 A dialog to set the sync options. 115 ''' 116
117 - def __init__(self, parent=None):
118 QDialog.__init__(self, parent) 119 # self.host = host 120 self.setWindowIcon(QIcon(":/icons/irondevil_sync.png")) 121 self.setWindowTitle('Sync') 122 self.verticalLayout = QVBoxLayout(self) 123 self.verticalLayout.setObjectName("verticalLayout") 124 self.resize(350, 190) 125 126 self.toolButton_SyncAll = QToolButton(self) 127 sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) 128 sizePolicy.setHorizontalStretch(0) 129 sizePolicy.setVerticalStretch(2) 130 sizePolicy.setHeightForWidth(self.toolButton_SyncAll.sizePolicy().hasHeightForWidth()) 131 self.toolButton_SyncAll.setSizePolicy(sizePolicy) 132 self.toolButton_SyncAll.setObjectName("toolButton_SyncAll") 133 self.verticalLayout.addWidget(self.toolButton_SyncAll) 134 self.toolButton_SyncAll.setText(self._translate("Sync All")) 135 self.toolButton_SyncAll.clicked.connect(self._on_sync_all_clicked) 136 137 # self.toolButton_SyncAllAnyMsg = QToolButton(self) 138 # sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) 139 # sizePolicy.setHorizontalStretch(0) 140 # sizePolicy.setVerticalStretch(1) 141 # sizePolicy.setHeightForWidth(self.toolButton_SyncAllAnyMsg.sizePolicy().hasHeightForWidth()) 142 # self.toolButton_SyncAllAnyMsg.setSizePolicy(sizePolicy) 143 # self.toolButton_SyncAllAnyMsg.setObjectName("toolButton_SyncAllAnyMsg") 144 # self.verticalLayout.addWidget(self.toolButton_SyncAllAnyMsg) 145 # self.toolButton_SyncAllAnyMsg.setText(self._translate("Sync all (+AnyMsg)")) 146 # self.toolButton_SyncAllAnyMsg.clicked.connect(self._on_sync_all_anymsg_clicked) 147 148 self.toolButton_SyncTopicOnDemand = QToolButton(self) 149 sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) 150 sizePolicy.setHorizontalStretch(0) 151 sizePolicy.setVerticalStretch(1) 152 sizePolicy.setHeightForWidth(self.toolButton_SyncTopicOnDemand.sizePolicy().hasHeightForWidth()) 153 self.toolButton_SyncTopicOnDemand.setSizePolicy(sizePolicy) 154 self.toolButton_SyncTopicOnDemand.setObjectName("toolButton_SyncTopicOnDemand") 155 self.verticalLayout.addWidget(self.toolButton_SyncTopicOnDemand) 156 self.toolButton_SyncTopicOnDemand.setText(self._translate("Sync only topics on demand")) 157 self.toolButton_SyncTopicOnDemand.clicked.connect(self._on_sync_topics_on_demand_clicked) 158 159 self.toolButton_SelectInterface = QToolButton(self) 160 sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) 161 sizePolicy.setHorizontalStretch(0) 162 sizePolicy.setVerticalStretch(1) 163 sizePolicy.setHeightForWidth(self.toolButton_SelectInterface.sizePolicy().hasHeightForWidth()) 164 self.toolButton_SelectInterface.setSizePolicy(sizePolicy) 165 self.toolButton_SelectInterface.setObjectName("toolButton_SelectInterface") 166 self.verticalLayout.addWidget(self.toolButton_SelectInterface) 167 self.toolButton_SelectInterface.setText(self._translate("Select an interface")) 168 self.toolButton_SelectInterface.clicked.connect(self._on_select_interface_clicked) 169 170 self.interface_field = QComboBox(self) 171 self.interface_field.setInsertPolicy(QComboBox.InsertAlphabetically) 172 self.interface_field.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)) 173 self.interface_field.setEditable(True) 174 self.interface_field.setVisible(False) 175 self.interface_field.setObjectName("interface_field") 176 self.verticalLayout.addWidget(self.interface_field) 177 self.interface_field.currentIndexChanged[str].connect(self._on_interface_selected) 178 179 self.toolButton_EditInterface = QToolButton(self) 180 sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) 181 sizePolicy.setHorizontalStretch(0) 182 sizePolicy.setVerticalStretch(1) 183 sizePolicy.setHeightForWidth(self.toolButton_EditInterface.sizePolicy().hasHeightForWidth()) 184 self.toolButton_EditInterface.setSizePolicy(sizePolicy) 185 self.toolButton_EditInterface.setObjectName("toolButton_EditInterface") 186 self.verticalLayout.addWidget(self.toolButton_EditInterface) 187 self.toolButton_EditInterface.setText(self._translate("Edit selected interface")) 188 self.toolButton_EditInterface.clicked.connect(self._on_edit_interface_clicked) 189 self.toolButton_EditInterface.setVisible(False) 190 191 self.toolButton_CreateInterface = QToolButton(self) 192 sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) 193 sizePolicy.setHorizontalStretch(0) 194 sizePolicy.setVerticalStretch(1) 195 sizePolicy.setHeightForWidth(self.toolButton_CreateInterface.sizePolicy().hasHeightForWidth()) 196 self.toolButton_CreateInterface.setSizePolicy(sizePolicy) 197 self.toolButton_CreateInterface.setObjectName("toolButton_CreateInterface") 198 self.verticalLayout.addWidget(self.toolButton_CreateInterface) 199 self.toolButton_CreateInterface.setText(self._translate("Create an interface")) 200 self.toolButton_CreateInterface.clicked.connect(self._on_create_interface_clicked) 201 self.toolButton_CreateInterface.setVisible(False) 202 203 self.textedit = TextEdit('', self) 204 self.hl = SyncHighlighter(self.textedit.document()) 205 sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) 206 self.textedit.setSizePolicy(sizePolicy) 207 self.textedit.setObjectName("syncedit") 208 self.verticalLayout.addWidget(self.textedit) 209 self.textedit.setVisible(False) 210 211 self._fill_interface_thread = None 212 self._interfaces_files = None 213 self._sync_args = [] 214 self._interface_filename = None 215 216 self.buttonBox = QDialogButtonBox(self) 217 self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel) 218 self.buttonBox.setOrientation(Qt.Horizontal) 219 self.buttonBox.setObjectName("buttonBox") 220 self.verticalLayout.addWidget(self.buttonBox) 221 self.buttonBox.accepted.connect(self.accept) 222 self.buttonBox.rejected.connect(self.reject) 223 self._new_iface = True
224
225 - def _translate(self, text):
226 if hasattr(QApplication, "UnicodeUTF8"): 227 return QApplication.translate("Form", text, None, QApplication.UnicodeUTF8) 228 else: 229 return QApplication.translate("Form", text, None)
230 231 @property
232 - def sync_args(self):
233 return self._sync_args
234 235 @property
236 - def interface_filename(self):
237 return self._interface_filename
238
239 - def _on_sync_all_clicked(self):
240 self.setResult(QDialog.Accepted) 241 self._sync_args = [] 242 self._sync_args.append(''.join(['_interface_url:=', "'.'"])) 243 self._sync_args.append(''.join(['_sync_topics_on_demand:=', 'False'])) 244 self._sync_args.append(''.join(['_ignore_hosts:=', '[]'])) 245 self._sync_args.append(''.join(['_sync_hosts:=', '[]'])) 246 self._sync_args.append(''.join(['_ignore_nodes:=', '[]'])) 247 self._sync_args.append(''.join(['_sync_nodes:=', '[]'])) 248 self._sync_args.append(''.join(['_ignore_topics:=', '[]'])) 249 self._sync_args.append(''.join(['_ignore_publishers:=', '[]'])) 250 self._sync_args.append(''.join(['_ignore_subscribers:=', '[]'])) 251 self._sync_args.append(''.join(['_sync_topics:=', '[]'])) 252 self._sync_args.append(''.join(['_ignore_services:=', '[]'])) 253 self._sync_args.append(''.join(['_sync_services:=', '[]'])) 254 self._sync_args.append(''.join(['_sync_remote_nodes:=', 'False'])) 255 self._interface_filename = None 256 self.accept()
257 258 # def _on_sync_all_anymsg_clicked(self): 259 # self._sync_args = [] 260 # self._sync_args.append(''.join(['_interface_url:=', "'.'"])) 261 # self._sync_args.append(''.join(['_sync_topics_on_demand:=', 'True'])) 262 # self._sync_args.append(''.join(['_ignore_hosts:=', '[]'])) 263 # self._sync_args.append(''.join(['_sync_hosts:=', '[]'])) 264 # self._sync_args.append(''.join(['_ignore_nodes:=', '[]'])) 265 # self._sync_args.append(''.join(['_sync_nodes:=', '[]'])) 266 # self._sync_args.append(''.join(['_ignore_topics:=', '[]'])) 267 # self._sync_args.append(''.join(['_sync_topics:=', '[/*]'])) 268 # self._sync_args.append(''.join(['_ignore_services:=', '[]'])) 269 # self._sync_args.append(''.join(['_sync_services:=', '[]'])) 270 # self._sync_args.append(''.join(['_sync_remote_nodes:=', 'False'])) 271 # self._interface_filename = None 272 # self.accept() 273
275 self._sync_args = [] 276 self._sync_args.append(''.join(['_interface_url:=', "'.'"])) 277 self._sync_args.append(''.join(['_sync_topics_on_demand:=', 'True'])) 278 self._sync_args.append(''.join(['_ignore_hosts:=', '[]'])) 279 self._sync_args.append(''.join(['_sync_hosts:=', '[]'])) 280 self._sync_args.append(''.join(['_ignore_nodes:=', '[]'])) 281 self._sync_args.append(''.join(['_sync_nodes:=', '[]'])) 282 self._sync_args.append(''.join(['_ignore_topics:=', '[]'])) 283 self._sync_args.append(''.join(['_ignore_publishers:=', '[]'])) 284 self._sync_args.append(''.join(['_ignore_subscribers:=', '[]'])) 285 self._sync_args.append(''.join(['_sync_topics:=', '[/only_on_demand]'])) 286 self._sync_args.append(''.join(['_ignore_services:=', '[/*]'])) 287 self._sync_args.append(''.join(['_sync_services:=', '[]'])) 288 self._sync_args.append(''.join(['_sync_remote_nodes:=', 'False'])) 289 self._interface_filename = None 290 self.accept()
291
293 self.toolButton_SyncAll.setVisible(False) 294 # self.toolButton_SyncAllAnyMsg.setVisible(False) 295 self.toolButton_SyncTopicOnDemand.setVisible(False) 296 self.toolButton_SelectInterface.setVisible(False) 297 self.interface_field.setVisible(True) 298 self.toolButton_CreateInterface.setVisible(True) 299 self.toolButton_EditInterface.setVisible(True) 300 self.toolButton_EditInterface.setEnabled(False) 301 self.textedit.setVisible(False) 302 # # fill the interfaces 303 if self._interfaces_files is None: 304 self.interface_field.addItems(['interface searching...']) 305 self.interface_field.setCurrentIndex(0) 306 self._fill_interface_thread = InterfacesThread() 307 self._fill_interface_thread.interfaces.connect(self._fill_interfaces) 308 self._fill_interface_thread.start() 309 else: 310 self.toolButton_EditInterface.setEnabled(self.interface_field.currentText() in self._interfaces_files) 311 self.buttonBox.clear() 312 self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) 313 self.interface_field.setFocus(Qt.TabFocusReason) 314 self.resize(350, 80)
315
316 - def _fill_interfaces(self, interfaces_files):
317 self._interfaces_files = interfaces_files 318 self.interface_field.clear() 319 self.interface_field.clearEditText() 320 self.interface_field.addItems(self._interfaces_files.keys())
321
322 - def _on_interface_selected(self, interface):
323 if self._interfaces_files and interface in self._interfaces_files: 324 self._sync_args = [] 325 self._sync_args.append(''.join(['_interface_url:=', interface])) 326 self.toolButton_EditInterface.setEnabled(True) 327 else: 328 self.toolButton_EditInterface.setEnabled(False)
329
330 - def accept(self):
331 if self.textedit.isVisible(): 332 try: 333 tmp_file = os.path.join(nm.screen().LOG_PATH, 'tmp_sync_interface.sync') 334 with open(tmp_file, 'w+') as f: 335 f.write(self.textedit.toPlainText()) 336 from master_discovery_fkie.common import read_interface 337 read_interface(tmp_file) 338 if not self._new_iface and self.interface_field.currentText() in self._interfaces_files: 339 fileName = self._interfaces_files[self.interface_field.currentText()] 340 else: 341 fileName, _ = QFileDialog.getSaveFileName(self, 'Save sync interface', '/home', "Sync Files (*.sync)") 342 if fileName: 343 with open(fileName, 'w+') as f: 344 self._interface_filename = fileName 345 f.write(self.textedit.toPlainText()) 346 if self._new_iface: 347 self.interface_field.clear() 348 self._interfaces_files = None 349 self._on_select_interface_clicked() 350 # QDialog.accept(self) 351 # self.resetView() 352 except Exception as e: 353 WarningMessageBox(QMessageBox.Warning, "Create sync interface", 354 "Error while create interface", 355 str(e)).exec_() 356 elif self.interface_field.isVisible(): 357 interface = self.interface_field.currentText() 358 if self._interfaces_files and interface in self._interfaces_files: 359 self._interface_filename = self._interfaces_files[interface] 360 self._sync_args = [] 361 self._sync_args.append(''.join(['_interface_url:=', interface])) 362 QDialog.accept(self) 363 self.resetView() 364 else: 365 QDialog.accept(self) 366 self.resetView()
367
368 - def reject(self):
369 if self.textedit.isVisible(): 370 self._on_select_interface_clicked() 371 else: 372 QDialog.reject(self) 373 self.resetView()
374
376 self._new_iface = True 377 self.interface_field.setVisible(False) 378 self.toolButton_CreateInterface.setVisible(False) 379 self.toolButton_EditInterface.setVisible(False) 380 self.textedit.setVisible(True) 381 self.textedit.setText("# The ignore_* lists will be processed first.\n" 382 "# For ignore/sync nodes, topics or services\n" 383 "# use follow declaration:\n" 384 "#{param name}: \n" 385 "# - {ros name}\n" 386 "# or for selected hosts:\n" 387 "# - {host name}:\n" 388 "# - {ros name}\n\n" 389 "# you can use follow wildcard: '*', but not as a first character\n" 390 "ignore_hosts:\n" 391 "sync_hosts:\n\n" 392 "ignore_nodes:\n" 393 "sync_nodes:\n\n" 394 "ignore_topics:\n" 395 "ignore_publishers:\n" 396 "ignore_subscribers:\n" 397 "sync_topics:\n\n" 398 "ignore_services:\n" 399 " - /*get_loggers\n" 400 " - /*set_logger_level\n" 401 "sync_services:\n\n" 402 "# If sync_topics_on_demand is True the local subscribed and published topics\n" 403 "# are synchronized with remote even if they are not in the sync_* list.\n" 404 "sync_topics_on_demand: False\n\n" 405 "# The nodes which are running not at the same host as the ROS master are not\n" 406 "# synchronized by default. Use sync_remote_nodes to sync these nodes also.\n" 407 "sync_remote_nodes: False\n\n" 408 ) 409 self.resize(350, 300)
410
412 self._new_iface = False 413 self.interface_field.setVisible(False) 414 self.toolButton_CreateInterface.setVisible(False) 415 self.toolButton_EditInterface.setVisible(False) 416 self.textedit.setVisible(True) 417 if self.interface_field.currentText() in self._interfaces_files: 418 try: 419 with open(self._interfaces_files[self.interface_field.currentText()], 'rw') as f: 420 iface = f.read() 421 self.textedit.setText(iface) 422 except Exception as e: 423 WarningMessageBox(QMessageBox.Warning, "Edit sync interface", 424 "Error while open interface", 425 str(e)).exec_() 426 self.resize(350, 300)
427
428 - def resetView(self):
429 self.toolButton_SyncAll.setVisible(True) 430 # self.toolButton_SyncAllAnyMsg.setVisible(True) 431 self.toolButton_SyncTopicOnDemand.setVisible(True) 432 self.toolButton_SelectInterface.setVisible(True) 433 self.interface_field.setVisible(False) 434 self.toolButton_CreateInterface.setVisible(False) 435 self.toolButton_EditInterface.setVisible(False) 436 self.textedit.setVisible(False) 437 self.buttonBox.clear() 438 self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel) 439 self.resize(350, 160)
440
441 442 -class InterfacesThread(QObject, threading.Thread):
443 ''' 444 A thread to list all sync interface files and 445 publish there be sending a QT signal. 446 ''' 447 interfaces = Signal(dict) 448 ''' 449 @ivar: interfaces is a signal, which is emitted, if a list with sync files was created. 450 ''' 451
452 - def __init__(self):
453 ''' 454 ''' 455 QObject.__init__(self) 456 threading.Thread.__init__(self) 457 self._interfaces_files = None 458 self.setDaemon(True)
459
460 - def run(self):
461 ''' 462 ''' 463 try: 464 # fill the input fields 465 self.root_paths = [os.path.normpath(p) for p in os.getenv("ROS_PACKAGE_PATH").split(':')] 466 self._interfaces_files = {} 467 for p in self.root_paths: 468 ret = self._getInterfaces(p) 469 self._interfaces_files = dict(ret.items() + self._interfaces_files.items()) 470 self.interfaces.emit(self._interfaces_files) 471 except: 472 import traceback 473 formatted_lines = traceback.format_exc(1).splitlines() 474 rospy.logwarn("Error while list sync interfaces:\n\t%s", formatted_lines[-1])
475
476 - def _getInterfaces(self, path, package=None):
477 result = {} 478 if os.path.isdir(path): 479 fileList = os.listdir(path) 480 # set package, if it is currently None and one found 481 if not package: 482 # detect package 483 if is_package(fileList): 484 package = os.path.basename(path) 485 for f in fileList: 486 ret = self._getInterfaces(os.path.join(path, f), package) 487 result = dict(ret.items() + result.items()) 488 elif package and os.path.isfile(path) and path.endswith('.sync'): 489 # create a selection for binaries 490 return {''.join(['pkg://', package, '///', os.path.basename(path)]): path} 491 return result
492