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  from __future__ import with_statement 
 18   
 19  import sys 
 20  import os 
 21  import traceback 
 22  import __builtin__ 
 23   
 24  from libxyz.core.utils import ustring 
 25  from libxyz.core.utils import is_func 
 26  from libxyz.core.plugins import Namespace 
 27  from libxyz.ui import Shortcut 
 28   
 29  import libxyz.exceptions as ex 
30 31 -def instantiated(func):
32 """ 33 Ensure the class has been instantiated 34 """ 35 36 def wrap(cls, *args, **kwargs): 37 if cls._instance is None: 38 error(_(u"Class must be instantiated first!")) 39 else: 40 return func(cls, *args, **kwargs)
41 42 wrap.__doc__ = func.__doc__ 43 44 return wrap 45
46 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 47 48 -def error(msg, trace=True):
49 if trace and hasattr(__builtin__, "xyzlog"): 50 xyzlog.debug(ustring(traceback.format_exc())) 51 raise ex.DSLError(_(u"DSL Error: %s") % msg)
52
53 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 54 55 -class XYZ(object):
56 """ 57 XYZ DSL implementation object 58 """ 59 60 api = ["let", 61 "val", 62 "unlet", 63 "load", 64 "bind", 65 "exec_file", 66 "kbd", 67 "action", 68 "macro", 69 "call", 70 "env", 71 "shell", 72 "alias", 73 "plugins_on", 74 "plugins_off", 75 "plugin_conf", 76 "icmd", 77 "prefix", 78 "help", 79 ] 80 81 macros = {} 82 83 _instance = None 84 _env = {} 85
86 - def __new__(cls, xyz):
87 if cls._instance is not None: 88 return cls._instance 89 90 # Else init singleton 91 cls.xyz = xyz 92 cls._instance = cls 93 94 cls._env = {"XYZ": cls} 95 cls._env.update(dict([(f, getattr(cls, f)) for f in cls.api])) 96 97 # Init macros 98 cls.macros["ACT_PATH"] = lambda: cls.xyz.pm.from_load( 99 ":sys:panel", "get_selected")().path 100 cls.macros["ACT_CWD"] = lambda: cls.xyz.pm.from_load( 101 ":sys:panel", "cwd")() 102 cls.macros["INACT_CWD"] = lambda: cls.xyz.pm.from_load( 103 ":sys:panel", "cwd_inactive")() 104 105 return cls
106 107 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 108 109 @classmethod 110 @instantiated
111 - def let(cls, var, val, sect=u"local"):
112 """ 113 Set variable. 114 Variable will be available in xyz.conf[section][varname] 115 If section is not provided - local will be used 116 """ 117 118 _conf = cls.xyz.conf 119 120 if sect not in _conf: 121 _conf[sect] = {} 122 123 if var in _conf[sect] and isinstance(_conf[sect][var], dict) and \ 124 isinstance(val, dict): 125 # Update rather than overwrite 126 _conf[sect][var].update(val) 127 else: 128 cls.xyz.conf[sect][var] = val
129 130 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 131 132 @classmethod 133 @instantiated
134 - def val(cls, var, sect=u"local"):
135 """ 136 Return variable value or None if undefined 137 """ 138 139 try: 140 return cls.xyz.conf[sect][var] 141 except Exception: 142 return None
143 144 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 145 146 @classmethod 147 @instantiated
148 - def unlet(cls, var, sect=u"local"):
149 """ 150 Unset variable 151 """ 152 153 if var in cls.xyz.conf[sect]: 154 del(cls.xyz.conf[sect])
155 156 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 157 158 @classmethod 159 @instantiated
160 - def load(cls, plugin):
161 """ 162 Load method[s] from plugin 163 """ 164 165 try: 166 cls.xyz.km.load(plugin) 167 except Exception, e: 168 error(_(u"Unable to load plugin %s: %s") % 169 (plugin, ustring(str(e))))
170 171 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 172 173 @classmethod 174 @instantiated
175 - def bind(cls, method, shortcut, context="DEFAULT"):
176 """ 177 Bind method to shortcut 178 """ 179 180 try: 181 cls.xyz.km.bind(method, shortcut, context=context) 182 except Exception, e: 183 error(_(u"Unable to bind shortcut: %s") % (ustring(str(e))))
184 185 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 186 187 @classmethod 188 @instantiated
189 - def kbd(cls, *args):
190 """ 191 Create keyboard shortcut 192 """ 193 194 return Shortcut(sc=list(args))
195 196 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 197 198 @classmethod 199 @instantiated
200 - def exec_file(cls, filename):
201 """ 202 Execute DSL in file 203 """ 204 205 with open(filename) as fh: 206 cls.execute(fh.read())
207 208 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 209 210 @classmethod 211 @instantiated
212 - def action(cls, rule, fn):
213 """ 214 Set up an action to be taken upon pressing action key on file 215 """ 216 217 try: 218 cls.xyz.am.register(rule, fn) 219 except Exception, e: 220 error(_(u"Unable to register action: %s") % ustring(str(e)))
221 222 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 223 224 @classmethod 225 @instantiated
226 - def macro(cls, macroname):
227 """ 228 Expand macro name. 229 230 Availbale macros: 231 * ACT_CWD -- Working directory in active panel 232 * INACT_CWD -- Working directory in inactive panel 233 * ACT_PATH -- Full selected object path in active panel 234 * INACT_PATH -- Full selected object path in inactive panel 235 * ACT_BASE -- Parent directory in active panel 236 * INACT_BASE -- Parent directory in inactive panel 237 * ACT_TAGGED -- List of tagged files in active panel 238 * INACT_TAGGED -- List of tagged files in inactive panel 239 * ACT_UNTAGGED -- List of not tagged files in active panel 240 * INACT_UNTAGGED -- List of not tagged files in inactive panel 241 """ 242 243 if macroname in cls.macros: 244 try: 245 return cls.macros[macroname]() 246 except Exception, e: 247 xyzlog.warning(_(u"Unable to expand macro %s: %s") % 248 (ustring(macroname), ustring(str(e)))) 249 # Return unchanged 250 return macroname
251 252 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 253 254 @classmethod 255 @instantiated
256 - def call(cls, method, *args):
257 """ 258 Call plugin method 259 """ 260 261 try: 262 p = Namespace(method) 263 m = cls.xyz.pm.from_load(p.pfull, p.method) 264 return m(*args) 265 except Exception, e: 266 error(_(u"Unable to execute method %s: %s" % 267 (method, ustring(str(e)))))
268 269 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 270 271 @classmethod 272 @instantiated
273 - def env(cls, var, default=None):
274 """ 275 Return environment variable or default if is not set 276 """ 277 278 return os.getenv(var, default)
279 280 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 281 282 @classmethod 283 @instantiated
284 - def shell(cls, cmd, *args, **kwargs):
285 """ 286 Execute command via :core:shell plugin 287 Optional boolean argument 'current' can be provided to indicate 288 that cmd is to be run from current directory. 289 Optional boolean argument 'bg' can be provided to indicate that cmd 290 must be executed in background 291 Optional boolean argument 'reload' can be provided to indicate 292 that panel content should/should not be reloaded after execution 293 294 """ 295 296 if kwargs.get("current", False): 297 cmd = "./%s" % cmd 298 299 bg = ["&"] if kwargs.get("bg", False) else [] 300 reloadp = kwargs.get("reload", True) 301 302 try: 303 exef = cls.xyz.pm.from_load(":core:shell", "execute") 304 escapef = cls.xyz.pm.from_load(":sys:cmd", "escape") 305 reloadf = cls.xyz.pm.from_load(":sys:panel", "reload_all") 306 exef(" ".join([escapef(cmd, True)] + 307 [escapef(a, True) for a in args] + bg)) 308 if reloadp: 309 reloadf() 310 except Exception, e: 311 error(_(u"Error in DSL shell execution: %s") % ustring(str(e)))
312 313 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 314 315 @classmethod 316 @instantiated
317 - def alias(cls, alias, replace):
318 """ 319 Set an alias which will be expanded in command line before execution 320 @param replace: Either string or function 321 """ 322 323 return cls.let(alias, replace, sect="aliases")
324 325 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 326 327 @classmethod 328 @instantiated
329 - def icmd(cls, command, obj):
330 """ 331 Set an internal command. 332 """ 333 334 if not is_func(obj): 335 error(_(u"Invalid object type: %s. Function expected") % 336 type(obj), trace=False) 337 338 return cls.let(command, obj, sect="commands")
339 340 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 341 342 @classmethod 343 @instantiated
344 - def plugins_on(cls, *plugins):
345 """ 346 Enable plugin[s] 347 """ 348 349 for plugin in plugins: 350 cls.let("plugins", {plugin: "ENABLE"}, sect="xyz")
351 352 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 353 354 @classmethod 355 @instantiated
356 - def plugins_off(cls, *plugins):
357 """ 358 Disable plugin[s] 359 """ 360 361 for plugin in plugins: 362 cls.let("plugins", {plugin: "DISABLE"}, sect="xyz")
363 364 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 365 366 @classmethod 367 @instantiated
368 - def plugin_conf(cls, plugin, opts):
369 """ 370 Configure plugin. 371 372 @param plugin: Plugin name 373 @param opts: Either tuple (var, val) or 374 dict {var1: val1, var2: var2,..} 375 """ 376 377 if isinstance(opts, tuple): 378 opts = dict([opts]) 379 380 if not isinstance(opts, dict): 381 cls.error(_(u"Invalid opts type: %s. Dict instance expected") 382 % type(opts)) 383 384 return cls.let(plugin, opts, sect="plugins")
385 386 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 387 388 @classmethod 389 @instantiated
390 - def prefix(cls, shortcut):
391 """ 392 Set new prefix key 393 """ 394 395 cls.xyz.km.set_prefix(shortcut)
396 397 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 398 399 @classmethod 400 @instantiated
401 - def help(cls, obj=None):
402 """ 403 Help 404 """ 405 406 fmt = lambda o: "%s\t%s" % (o, getattr(cls, o).__doc__) 407 408 if obj is not None and obj not in cls.api: 409 error(_(u"Invalid function %s") % obj) 410 411 objs = [obj] if obj else cls.api 412 413 return "\n".join([fmt(x) for x in objs]).replace("\t", " ")
414 415 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 416 417 @classmethod 418 @instantiated
419 - def execute(cls, source):
420 """ 421 Execute DSL statements 422 @param source: Either string or open file-object or code object 423 """ 424 425 try: 426 exec source in cls._env.copy() 427 except Exception, e: 428 error(_(u"Error in DSL execution: %s") % ustring(str(e)))
429 430 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 431 432 @classmethod 433 @instantiated
434 - def get_env(cls):
435 """ 436 Return copy of global dsl environment 437 """ 438 439 return cls._env.copy()
440 441 #++++++++++++++++++++++++++++++++++++++++++++++++ 442 443 ## Auto-generate corresponding module-level functions 444 module = sys.modules[__name__] 445 446 __all__ = ["XYZ"] 447 448 for f in XYZ.api: 449 setattr(module, f, getattr(XYZ, f)) 450 __all__.append(f) 451