Package libxyz :: Package core :: Module dsl
[hide private]
[frames] | no frames]

Source Code for Module libxyz.core.dsl

  1  #-*- coding: utf8 -* 
  2  # 
  3  # Max E. Kuznecov ~syhpoon <mek@mek.uz.ua> 2008-2009 
  4  # 
  5  # This file is part of XYZCommander. 
  6  # XYZCommander is free software: you can redistribute it and/or modify 
  7  # it under the terms of the GNU Lesser Public License as published by 
  8  # the Free Software Foundation, either version 3 of the License, or 
  9  # (at your option) any later version. 
 10  # XYZCommander is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 13  # GNU Lesser Public License for more details. 
 14  # You should have received a copy of the GNU Lesser Public License 
 15  # along with XYZCommander. If not, see <http://www.gnu.org/licenses/>. 
 16   
 17  import sys 
 18  import os 
 19  import traceback 
 20  import __builtin__ 
 21   
 22  from libxyz.core.utils import ustring 
 23  from libxyz.core.utils import is_func 
 24  from libxyz.core.plugins import Namespace 
 25  from libxyz.core import FSRule 
 26   
 27  from libxyz.ui.colors import Palette 
 28  from libxyz.ui import Shortcut 
 29   
 30  from skin import Skin 
 31   
 32  import libxyz.exceptions as ex 
33 34 -def instantiated(func):
35 """ 36 Ensure the class has been instantiated 37 """ 38 39 def wrap(cls, *args, **kwargs): 40 if cls._instance is None: 41 error(_(u"Class must be instantiated first!")) 42 else: 43 return func(cls, *args, **kwargs)
44 45 wrap.__doc__ = func.__doc__ 46 47 return wrap 48
49 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 50 51 -def error(msg, trace=True):
52 if trace and hasattr(__builtin__, "xyzlog"): 53 xyzlog.debug(ustring(traceback.format_exc())) 54 raise ex.DSLError(_(u"DSL Error: %s") % msg)
55
56 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 57 58 -class XYZ(object):
59 """ 60 XYZ DSL implementation object 61 """ 62 63 api = ["let", 64 "val", 65 "section", 66 "unlet", 67 "load", 68 "bind", 69 "exec_file", 70 "kbd", 71 "action", 72 "macro", 73 "call", 74 "env", 75 "shell", 76 "alias", 77 "plugins_on", 78 "plugins_off", 79 "plugin_conf", 80 "icmd", 81 "prefix", 82 "help", 83 "vfs", 84 "vfs_path", 85 "hook", 86 "unhook", 87 "fsrule", 88 "palette", 89 "skin" 90 ] 91 92 EVENT_CONF_UPDATE = u"event:conf_update" 93 94 macros = {} 95 96 _instance = None 97 _env = {} 98
99 - def __new__(cls, xyz):
100 if cls._instance is not None: 101 return cls._instance 102 103 # Else init singleton 104 cls.xyz = xyz 105 cls._instance = cls 106 107 cls._env = {"XYZ": cls} 108 cls._env.update(dict([(ff, getattr(cls, ff)) for ff in cls.api])) 109 110 # Init macros 111 cls.macros = {} 112 cls.init_macros() 113 return cls
114 115 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 116 117 @classmethod
118 - def init_macros(cls):
119 cls.macros["ACT_CWD"] = lambda: cls.xyz.pm.from_load(":sys:panel", 120 "cwd")() 121 cls.macros["INACT_CWD"] = lambda: cls.xyz.pm.from_load(":sys:panel", 122 "cwd")(False) 123 124 cls.macros["ACT_PATH"] = lambda: \ 125 cls.xyz.pm.from_load(":sys:panel", 126 "get_selected")().path 127 128 cls.macros["INACT_PATH"] = lambda: \ 129 cls.xyz.pm.from_load(":sys:panel", 130 "get_selected" 131 )(False).path 132 cls.macros["ACT_BASE"] = lambda: \ 133 os.path.dirname(cls.macros["ACT_CWD"]()) 134 135 cls.macros["INACT_BASE"] = lambda: \ 136 os.path.dirname(cls.macros["INACT_CWD"]()) 137 138 cls.macros["ACT_TAGGED"] = lambda: [x.full_path for x in 139 cls.xyz.pm.from_load( 140 ":sys:panel", 141 "get_tagged")()] 142 cls.macros["INACT_TAGGED"] = lambda: [x.full_path for x in 143 cls.xyz.pm.from_load( 144 ":sys:panel", 145 "get_tagged")(False)] 146 147 cls.macros["ACT_UNTAGGED"] = lambda: [x.full_path for x in 148 cls.xyz.pm.from_load( 149 ":sys:panel", 150 "get_untagged")()] 151 cls.macros["INACT_UNTAGGED"] = lambda: [x.full_path for x in 152 cls.xyz.pm.from_load( 153 ":sys:panel", 154 "get_untagged")(False)]
155 156 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 157 158 @classmethod
159 - def _clear(cls):
160 cls._instance = None
161 162 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 163 164 @classmethod 165 @instantiated
166 - def let(cls, var, val, sect=u"local"):
167 """ 168 Set variable. 169 Variable will be available in xyz.conf[section][varname] 170 If section is not provided - local will be used 171 """ 172 173 _conf = cls.xyz.conf 174 175 if sect not in _conf: 176 _conf[sect] = {} 177 178 if var in _conf[sect] and isinstance(_conf[sect][var], dict) and \ 179 isinstance(val, dict): 180 # Update rather than overwrite 181 _conf[sect][var].update(val) 182 else: 183 cls.xyz.conf[sect][var] = val 184 185 cls.xyz.hm.dispatch(cls.EVENT_CONF_UPDATE, var, val, sect)
186 187 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 188 189 @classmethod 190 @instantiated
191 - def val(cls, var, sect=u"local"):
192 """ 193 Return variable value or None if undefined 194 """ 195 196 try: 197 return cls.xyz.conf[sect][var] 198 except Exception: 199 return None
200 201 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 202 203 @classmethod 204 @instantiated
205 - def section(cls, sect=u"local"):
206 """ 207 Return whole configuration section contents as a dictionary or None 208 if undefined 209 """ 210 211 try: 212 return cls.xyz.conf[sect] 213 except Exception: 214 return None
215 216 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 217 218 @classmethod 219 @instantiated
220 - def unlet(cls, var, sect=u"local"):
221 """ 222 Unset variable 223 """ 224 225 if var in cls.xyz.conf[sect]: 226 del(cls.xyz.conf[sect])
227 228 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 229 230 @classmethod 231 @instantiated
232 - def load(cls, plugin):
233 """ 234 Load method[s] from plugin 235 """ 236 237 try: 238 cls.xyz.km.load(plugin) 239 except Exception, e: 240 error(_(u"Unable to load plugin %s: %s") % 241 (plugin, ustring(str(e))))
242 243 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 244 245 @classmethod 246 @instantiated
247 - def bind(cls, method, shortcut, context="DEFAULT"):
248 """ 249 Bind method to shortcut 250 """ 251 252 try: 253 cls.xyz.km.bind(method, shortcut, context=context) 254 except Exception, e: 255 error(_(u"Unable to bind shortcut %s: %s") % (str(shortcut), 256 ustring(str(e))))
257 258 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 259 260 @classmethod 261 @instantiated
262 - def kbd(cls, *args):
263 """ 264 Create keyboard shortcut 265 """ 266 267 return Shortcut(sc=list(args))
268 269 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 270 271 @classmethod 272 @instantiated
273 - def exec_file(cls, filename):
274 """ 275 Execute DSL in file 276 """ 277 278 f = None 279 280 try: 281 f = open(filename) 282 cls.execute(f.read()) 283 except Exception, e: 284 error(_(u"Unable to execute file%s") % ( 285 ustring(str(e)))) 286 287 if f: 288 f.close()
289 290 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 291 292 @classmethod 293 @instantiated
294 - def action(cls, rule, fn):
295 """ 296 Set up an action to be taken upon pressing action key on file 297 """ 298 299 try: 300 cls.xyz.am.register(rule, fn) 301 except Exception, e: 302 error(_(u"Unable to register action: %s") % ustring(str(e)))
303 304 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 305 306 @classmethod 307 @instantiated
308 - def macro(cls, macroname):
309 """ 310 Expand macro name. 311 312 Availbale macros: 313 * ACT_CWD -- Working directory in active panel 314 * INACT_CWD -- Working directory in inactive panel 315 * ACT_PATH -- Full selected object path in active panel 316 * INACT_PATH -- Full selected object path in inactive panel 317 * ACT_BASE -- Parent directory in active panel 318 * INACT_BASE -- Parent directory in inactive panel 319 * ACT_TAGGED -- List of tagged files in active panel 320 * INACT_TAGGED -- List of tagged files in inactive panel 321 * ACT_UNTAGGED -- List of not tagged files in active panel 322 * INACT_UNTAGGED -- List of not tagged files in inactive panel 323 """ 324 325 if macroname in cls.macros: 326 try: 327 return cls.macros[macroname]() 328 except Exception, e: 329 xyzlog.warning(_(u"Unable to expand macro %s: %s") % 330 (ustring(macroname), ustring(str(e)))) 331 # Return unchanged 332 return macroname
333 334 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 335 336 @classmethod 337 @instantiated
338 - def call(cls, method, *args, **kwargs):
339 """ 340 Call plugin method 341 """ 342 343 try: 344 p = Namespace(method) 345 m = cls.xyz.pm.from_load(p.pfull, p.method) 346 return m(*args, **kwargs) 347 except Exception, e: 348 error(_(u"Unable to execute method %s: %s" % 349 (method, ustring(str(e)))))
350 351 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 352 353 @classmethod 354 @instantiated
355 - def env(cls, var, default=None):
356 """ 357 Return environment variable or default if is not set 358 """ 359 360 return os.getenv(var, default)
361 362 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 363 364 @classmethod 365 @instantiated
366 - def shell(cls, cmd, *args, **kwargs):
367 """ 368 Execute command via :core:shell plugin 369 Optional boolean argument 'current' can be provided to indicate 370 that cmd is to be run from current directory. 371 Optional boolean argument 'bg' can be provided to indicate that cmd 372 must be executed in background 373 Optional boolean argument 'reload' can be provided to indicate 374 that panel content should/should not be reloaded after execution 375 Optional boolean argument 'wait' can be provided to indicate 376 that shell should/should not wait for user input after command executed 377 The wait flag has higher priority than :core:shell's `wait` 378 configuration flag. 379 """ 380 381 if kwargs.get("current", False): 382 cmd = "./%s" % cmd 383 384 if kwargs.get("bg", False): 385 bg = ["&"] 386 else: 387 bg = [] 388 389 reloadp = kwargs.get("reload", True) 390 wait = kwargs.get("wait", None) 391 392 try: 393 exef = cls.xyz.pm.from_load(":core:shell", "execute") 394 escapef = cls.xyz.pm.from_load(":sys:cmd", "escape") 395 reloadf = cls.xyz.pm.from_load(":sys:panel", "reload_all") 396 exef(" ".join([escapef(cmd, True)] + 397 [escapef(a, True) for a in args] + bg), wait=wait) 398 if reloadp: 399 reloadf() 400 except Exception, e: 401 error(_(u"Error in DSL shell execution: %s") % ustring(str(e)))
402 403 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 404 405 @classmethod 406 @instantiated
407 - def alias(cls, alias, replace):
408 """ 409 Set an alias which will be expanded in command line before execution 410 @param replace: Either string or function 411 """ 412 413 return cls.let(alias, replace, sect="aliases")
414 415 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 416 417 @classmethod 418 @instantiated
419 - def icmd(cls, command, obj):
420 """ 421 Set an internal command. 422 """ 423 424 if not is_func(obj): 425 error(_(u"Invalid object type: %s. Function expected") % 426 type(obj), trace=False) 427 428 return cls.let(command, obj, sect="commands")
429 430 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 431 432 @classmethod 433 @instantiated
434 - def plugins_on(cls, *plugins):
435 """ 436 Enable plugin[s] 437 """ 438 439 for plugin in plugins: 440 cls.let("plugins", {plugin: "ENABLE"}, sect="xyz")
441 442 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 443 444 @classmethod 445 @instantiated
446 - def plugins_off(cls, *plugins):
447 """ 448 Disable plugin[s] 449 """ 450 451 for plugin in plugins: 452 cls.let("plugins", {plugin: "DISABLE"}, sect="xyz")
453 454 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 455 456 @classmethod 457 @instantiated
458 - def plugin_conf(cls, plugin, opts):
459 """ 460 Configure plugin. 461 462 @param plugin: Plugin name 463 @param opts: dict {var1: val1, var2: var2,..} 464 """ 465 466 if not isinstance(opts, dict): 467 error(_(u"Invalid opts type: %s. Dict instance expected") 468 % type(opts)) 469 470 return cls.let(plugin, opts, sect="plugins")
471 472 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 473 474 @classmethod 475 @instantiated
476 - def prefix(cls, shortcut):
477 """ 478 Set new prefix key 479 """ 480 481 cls.xyz.km.set_prefix(shortcut)
482 483 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 484 485 @classmethod 486 @instantiated
487 - def help(cls, obj=None):
488 """ 489 Help 490 """ 491 492 fmt = lambda o: "%s\t%s" % (o, getattr(cls, o).__doc__) 493 494 if obj is not None and obj not in cls.api: 495 error(_(u"Invalid function %s") % obj) 496 497 if obj: 498 objs = [obj] 499 else: 500 objs = cls.api 501 502 return "\n".join([fmt(x) for x in objs]).replace("\t", " ")
503 504 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 505 506 @classmethod 507 @instantiated
508 - def vfs(cls, prefix, vfsclass):
509 """ 510 Set prefix and VFSObject class for VFS dispatching 511 """ 512 513 try: 514 return cls.xyz.vfs.register(prefix, vfsclass) 515 except Exception, e: 516 error(_(u"Error setting VFS prefix: %s") % ustring(str(e)))
517 518 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 519 520 @classmethod 521 @instantiated
522 - def vfs_path(cls, path, driver):
523 """ 524 Construct path using provided VFS driver 525 """ 526 527 return path + "#vfs-%s#" % driver
528 529 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 530 531 @classmethod 532 @instantiated
533 - def hook(cls, event, proc):
534 """ 535 Register a new hook. 536 Event is an event string and proc is a procedure to be called 537 """ 538 539 try: 540 return cls.xyz.hm.register(event, proc) 541 except Exception, e: 542 error(_(u"Error registering new hook: %s") % ustring(str(e)))
543 544 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 545 546 @classmethod 547 @instantiated
548 - def unhook(cls, event):
549 """ 550 Remove all hooks for the event 551 """ 552 553 return cls.xyz.hm.clear(event)
554 555 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 556 557 @classmethod 558 @instantiated
559 - def fsrule(cls, rule):
560 """ 561 Return libxyz.core.FSRule instance 562 """ 563 564 try: 565 return FSRule(rule) 566 except Exception, e: 567 error(_(u"Error parsing FSRule: %s") % ustring(str(e)))
568 569 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 570 571 @classmethod 572 @instantiated
573 - def palette(cls, config):
574 """ 575 Create internal palette object 576 577 @param config: Dictionary of form: 578 { 579 'foreground': COLOR, 580 'background': COLOR, 581 'fg_attributes': [ATTR], 582 'mono': [ATTR], 583 'foreground_high': HG_COLOR, 584 'background_high': HG_COLOR 585 } 586 """ 587 588 try: 589 return Palette(None, *Palette.convert(config)) 590 except Exception, e: 591 error(_(u"Error creating Palette instance: %s") % ustring(str(e)))
592 593 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 594 595 @classmethod 596 @instantiated
597 - def skin(cls, **kwargs):
598 """ 599 Make and register new skin 600 """ 601 602 try: 603 cls.xyz.sm.add(Skin(**kwargs)) 604 except Exception, e: 605 error(_(u"Error creating Skin instance: %s") % ustring(str(e)))
606 607 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 608 609 @classmethod 610 @instantiated
611 - def execute(cls, source):
612 """ 613 Execute DSL statements 614 @param source: Either string or open file-object or code object 615 """ 616 617 try: 618 exec source in cls._env.copy() 619 except Exception, e: 620 error(_(u"Error in DSL execution: %s") % ustring(str(e)))
621 622 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 623 624 @classmethod 625 @instantiated
626 - def get_env(cls):
627 """ 628 Return copy of global dsl environment 629 """ 630 631 return cls._env.copy()
632 633 #++++++++++++++++++++++++++++++++++++++++++++++++ 634 635 ## Auto-generate corresponding module-level functions 636 module = sys.modules[__name__] 637 638 __all__ = ["XYZ"] 639 640 for f in XYZ.api: 641 setattr(module, f, getattr(XYZ, f)) 642 __all__.append(f) 643