229 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			229 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # coding=utf-8
 | |
| import sys
 | |
| import os
 | |
| import os.path
 | |
| import codecs
 | |
| import json
 | |
| import gc
 | |
| import imp
 | |
| import re
 | |
| from file import File
 | |
| 
 | |
| BASE_PATH = os.path.abspath(os.path.dirname(__file__))
 | |
| is_python3 = sys.version_info[0] > 2
 | |
| 
 | |
| core_files = ['emmet-app.js', 'python-wrapper.js']
 | |
| 
 | |
| def should_use_unicode():
 | |
| 	"""
 | |
| 	WinXP unable to eval JS in unicode object (while other OSes requires it)
 | |
| 	This function checks if we have to use unicode when reading files
 | |
| 	"""
 | |
| 	ctx = PyV8.JSContext()
 | |
| 	ctx.enter()
 | |
| 	use_unicode = True
 | |
| 	try:
 | |
| 		ctx.eval(u'(function(){return;})()')
 | |
| 	except:
 | |
| 		use_unicode = False
 | |
| 
 | |
| 	ctx.leave()
 | |
| 
 | |
| 	return use_unicode
 | |
| 
 | |
| def make_path(filename):
 | |
| 	return os.path.normpath(os.path.join(BASE_PATH, filename))
 | |
| 
 | |
| def js_log(message):
 | |
| 	print(message)
 | |
| 
 | |
| def js_file_reader(file_path, use_unicode=True):
 | |
| 	if use_unicode:
 | |
| 		f = codecs.open(file_path, 'r', 'utf-8')
 | |
| 	else:
 | |
| 		f = open(file_path, 'r')
 | |
| 
 | |
| 	content = f.read()
 | |
| 	f.close()
 | |
| 
 | |
| 	return content
 | |
| 
 | |
| def import_pyv8():
 | |
| 	# Importing non-existing modules is a bit tricky in Python:
 | |
| 	# if we simply call `import PyV8` and module doesn't exists,
 | |
| 	# Python will cache this failed import and will always
 | |
| 	# throw exception even if this module appear in PYTHONPATH.
 | |
| 	# To prevent this, we have to manually test if 
 | |
| 	# PyV8.py(c) exists in PYTHONPATH before importing PyV8
 | |
| 	if 'PyV8' in sys.modules:
 | |
| 		# PyV8 was loaded by ST2, create global alias
 | |
| 		if 'PyV8' not in globals():
 | |
| 			globals()['PyV8'] = __import__('PyV8')
 | |
| 			
 | |
| 		return
 | |
| 
 | |
| 	loaded = False
 | |
| 	f, pathname, description = imp.find_module('PyV8')
 | |
| 	bin_f, bin_pathname, bin_description = imp.find_module('_PyV8')
 | |
| 	if f:
 | |
| 		try:
 | |
| 			imp.acquire_lock()
 | |
| 			globals()['_PyV8'] = imp.load_module('_PyV8', bin_f, bin_pathname, bin_description)
 | |
| 			globals()['PyV8'] = imp.load_module('PyV8', f, pathname, description)
 | |
| 			imp.release_lock()
 | |
| 			loaded = True
 | |
| 		finally:
 | |
| 			# Since we may exit via an exception, close fp explicitly.
 | |
| 			if f:
 | |
| 				f.close()
 | |
| 			if bin_f:
 | |
| 				bin_f.close()
 | |
| 
 | |
| 	if not loaded:
 | |
| 		raise ImportError('No PyV8 module found')
 | |
| 	
 | |
| 
 | |
| class Context():
 | |
| 	"""
 | |
| 	Creates Emmet JS core context.
 | |
| 	Before instantiating this class, make sure PyV8
 | |
| 	is available in `sys.path`
 | |
| 	
 | |
| 	@param files: Additional files to load with JS core
 | |
| 	@param path: Path to Emmet extensions
 | |
| 	@param contrib: Python objects to contribute to JS execution context
 | |
| 	@param pyv8_path: Location of PyV8 binaries
 | |
| 	"""
 | |
| 	def __init__(self, files=[], ext_path=None, contrib=None, logger=None, reader=js_file_reader):
 | |
| 		self.logger = logger
 | |
| 		self.reader = reader
 | |
| 
 | |
| 		try:
 | |
| 			import_pyv8()
 | |
| 		except ImportError as e:
 | |
| 			pass
 | |
| 
 | |
| 		self._ctx = None
 | |
| 		self._contrib = contrib
 | |
| 		self._should_load_extension = True
 | |
| 
 | |
| 		# detect reader encoding
 | |
| 		self._use_unicode = None
 | |
| 		self._core_files = [] + core_files + files
 | |
| 
 | |
| 		self._ext_path = None
 | |
| 		self.set_ext_path(ext_path)
 | |
| 		self._user_data = None
 | |
| 
 | |
| 	def log(self, message):
 | |
| 		if self.logger:
 | |
| 			self.logger(message)
 | |
| 		
 | |
| 	def get_ext_path(self):
 | |
| 		return self._ext_path
 | |
| 
 | |
| 	def set_ext_path(self, val):
 | |
| 		try:
 | |
| 			if val and val[:1] == '~':
 | |
| 				val = os.path.expanduser(val)
 | |
| 
 | |
| 			val = os.path.abspath(val)
 | |
| 		except Exception as e:
 | |
| 			return
 | |
| 
 | |
| 		if val == self._ext_path:
 | |
| 			return
 | |
| 
 | |
| 		self._ext_path = val
 | |
| 		self.reset()
 | |
| 
 | |
| 	def load_extensions(self, path=None):
 | |
| 		if path is None:
 | |
| 			path = self._ext_path;
 | |
| 
 | |
| 		if path and os.path.isdir(path):
 | |
| 			ext_files = []
 | |
| 			self.log('Loading Emmet extensions from %s' % self._ext_path)
 | |
| 			for dirname, dirnames, filenames in os.walk(self._ext_path):
 | |
| 				for filename in filenames:
 | |
| 					if filename[0] != '.':
 | |
| 						ext_files.append(os.path.join(dirname, filename))
 | |
| 
 | |
| 			self.js().locals.pyLoadExtensions(ext_files)
 | |
| 
 | |
| 	def js(self):
 | |
| 		"Returns JS context"
 | |
| 		if not self._ctx:
 | |
| 			try:
 | |
| 				import_pyv8()
 | |
| 			except ImportError as e:
 | |
| 				return None 
 | |
| 
 | |
| 			if 'PyV8' not in sys.modules:
 | |
| 				# Binary is not available yet
 | |
| 				return None
 | |
| 
 | |
| 			if self._use_unicode is None:
 | |
| 				self._use_unicode = should_use_unicode()
 | |
| 			
 | |
| 			glue = u'\n' if self._use_unicode else '\n'
 | |
| 			core_src = [self.read_js_file(make_path(f)) for f in self._core_files]
 | |
| 
 | |
| 			self._ctx = PyV8.JSContext()
 | |
| 			self._ctx.enter()
 | |
| 			self._ctx.eval(glue.join(core_src))
 | |
| 
 | |
| 			# for f in self._core_files:
 | |
| 			# 	self._ctx.eval(self.read_js_file(make_path(f)), name=f, line=0, col=0)
 | |
| 
 | |
| 			# load default snippets
 | |
| 			self._ctx.locals.pyLoadSystemSnippets(self.read_js_file(make_path('snippets.json')))
 | |
| 
 | |
| 			# expose some methods
 | |
| 			self._ctx.locals.log = js_log
 | |
| 			self._ctx.locals.pyFile = File()
 | |
| 
 | |
| 			if self._contrib:
 | |
| 				for k in self._contrib:
 | |
| 					self._ctx.locals[k] = self._contrib[k]
 | |
| 		else:
 | |
| 			self._ctx.enter()
 | |
| 
 | |
| 		if self._should_load_extension:
 | |
| 			self._ctx.locals.pyResetUserData()
 | |
| 			self._should_load_extension = False
 | |
| 			self.load_extensions()
 | |
| 
 | |
| 		if self._user_data:
 | |
| 			self._ctx.locals.pyLoadUserData(self._user_data)
 | |
| 			self._user_data = None
 | |
| 
 | |
| 		return self._ctx
 | |
| 
 | |
| 	def load_user_data(self, data):
 | |
| 		"Loads user data payload from JSON"
 | |
| 		self._user_data = data
 | |
| 		# self.js().locals.pyLoadUserData(data)
 | |
| 
 | |
| 	def reset(self):
 | |
| 		"Resets JS execution context"
 | |
| 		if self._ctx:
 | |
| 			self._ctx.leave()
 | |
| 			self._ctx = None
 | |
| 			try:
 | |
| 				PyV8.JSEngine.collect()
 | |
| 				gc.collect()
 | |
| 			except:
 | |
| 				pass
 | |
| 
 | |
| 		self._should_load_extension = True
 | |
| 
 | |
| 	def read_js_file(self, file_path):
 | |
| 		return self.reader(file_path, self._use_unicode)
 | |
| 
 | |
| 	def eval(self, source):
 | |
| 		self.js().eval(source)
 | |
| 
 | |
| 	def eval_js_file(self, file_path):
 | |
| 		self.eval(self.read_js_file(file_path))
 |