# -*- coding: utf-8 -*- """ Module that can dynamically generate stubs. """ from __future__ import print_function, absolute_import, with_statement, unicode_literals, division import sys class RawJS(object): """ An object to wrap verbatim code to be included in the generated JavaScript. This serves a number of purposes: * Using code in PScript that is not valid Python syntax, like regular expressions or the jQuery object ``$``. * Write high performance code that avoids Pythonic features like operator overloading. * In Flexx's module system it can be used to create a stub variable in Python that *does* have a value in JS. This value can imported in other modules, leading to a shared value also in JS. PScript does not verify the syntax of the code, so write carefully! To allow the features in the 3d point, this object has a magic touch: the ``__module__`` attribute of an instance refers to the module in which it was instantiated, and if it's a global, its defining name can be obtained. Example: .. code-block:: py # Syntax not usable in Py myre = RawJS('/ab+c/') # Code that should only execute on JS foo = RawJS('require("some.module")') # Performance def bar(n): res = [] RawJS(''' for (var i=0; i' % (self.__class__.__name__, self.get_code(0)) else: return '<%s with %i lines>' % (self.__class__.__name__, len(self._lines)) def __str__(self): return self.get_code(0) @classmethod def _str2lines(cls, text): """ Classmethod to split a text in lines, dedenting each line. The first line's indentation will assume the minimal indentation. """ lines = text.replace('\r', '').split('\n') lines[0] = lines[0].strip() # firts line is always detented if len(lines) > 1: # Get minimal indentation min_indent = 99999 for line in lines[1:]: if line.strip(): # don't count empty lines min_indent = min(min_indent, len(line) - len(line.lstrip())) # Remove indentation for i in xrange(1, len(lines)): lines[i] = lines[i][min_indent:].rstrip() # Remove empty line only at beginning if not lines[0]: lines.pop(0) return lines def get_defined_name(self, suggestion=None): """ Get the name by which this object is known in the module in which it is defined. Only works if it is a global. Returns '' otherwise. If a suggestion is given and it is the correct name, this function performs faster. The resulting name is cached internally. """ if self._real_name is None: self._real_name = '' # could be defined not in the globals if suggestion and self._globals.get(suggestion, None) is self: self._real_name = suggestion else: for name, val in self._globals.items(): if val is self: self._real_name = name break return self._real_name def get_code(self, indent=0): """ Get the code with the given indentation. """ indent = indent * ' ' return '\n'.join([indent + line for line in self._lines]) class JSConstant(object): """ Class to represent variables that are used in JS, and are considered global or otherwise available in a way that Python cannot know. """ def __init__(self, name='jsconstant'): self._name = name def __repr__(self): # pragma: no cover return '<%s %s>' % (self.__class__.__name__, self._name) class Stubs(object): __name__ = __name__ __file__ = __file__ JSConstant = JSConstant RawJS = RawJS def __getattr__(self, name): if name in ('JSConstant', 'RawJS'): return getattr(self, name) else: return self.JSConstant(name) # Seems hacky, but is supported: http://stackoverflow.com/a/7668273/2271927 sys.modules[__name__] = Stubs()