def resume(self): """Resumes the session after it has been paused.""" Console.info("Resuming session...") for project in self.__projects: project.resume()
def build(self): """Build static website.""" Console.info("Intializing Konstrukteur...") Console.indent() # Path configuration # TODO: Use Jasy configuration instead self.__templatePath = os.path.join("source", "template") self.__contentPath = os.path.join("source", "content") self.__sourcePath = os.path.join("source") self.__pagePath = os.path.join(self.__contentPath, "page") self.__postPath = os.path.join(self.__contentPath, "post") if not os.path.exists(self.__templatePath): raise RuntimeError("Path to templates not found : %s" % self.__templatePath) if not os.path.exists(self.__contentPath): raise RuntimeError("Path to content not found : %s" % self.__contentPath) # A theme could be any project registered in the current session if self.__theme: themeProject = session.getProjectByName(self.__theme) if not themeProject: raise RuntimeError("Theme '%s' not found" % self.__theme) self.__initializeTemplates() self.__generateOutput()
def deploy(self, classes, assetFolder=None): """ Deploys all asset files to the destination asset folder. This merges assets from different projects into one destination folder. """ # Sometimes it's called with explicit None - we want to fill the default # in that case as well. if assetFolder is None: assetFolder = "$prefix/asset" assets = self.__assets projects = self.__session.getProjects() copyAssetFolder = self.__session.expandFileName(assetFolder) filterExpr = self.__compileFilterExpr(classes) Console.info("Deploying assets...") counter = 0 length = len(assets) for fileId in assets: if not filterExpr.match(fileId): length -= 1 continue srcFile = assets[fileId].getPath() dstFile = os.path.join(copyAssetFolder, fileId.replace("/", os.sep)) if jasy.core.File.syncfile(srcFile, dstFile): counter += 1 Console.info("Updated %s/%s files" % (counter, length))
def __generateFeed(self): if not self.__posts: return Console.info("Generating feed...") Console.indent() itemsInFeed = self.config["blog"]["itemsInFeed"] destinationPath = self.__profile.getDestinationPath() for language in self.__languages: sortedPosts = self.__getSortedPosts(language) # Feed Render Model renderModel = { 'config' : self.config, 'site' : { 'name' : self.__siteName, 'url' : self.__siteUrl }, "current" : { "lang" : language }, "feedUrl" : self.__feedUrl, "now" : datetime.datetime.now(tz=dateutil.tz.tzlocal()).replace(microsecond=0).isoformat(), "posts" : sortedPosts[0:itemsInFeed] } template = self.__templates["%s.Feed" % self.__theme] outputContent = template.render(renderModel) outputFilename = os.path.join(destinationPath, self.__feedUrl) self.__fileManager.writeFile(outputFilename, outputContent) Console.outdent()
def __generateTranslationBundle(self): """ Returns a translation object for the given language containing all relevant translation files for the current project set. """ language = self.getCurrentPermutation().get("locale") if language is None: return None if language in self.__translationBundles: return self.__translationBundles[language] Console.info("Creating translation bundle: %s", language) Console.indent() # Initialize new Translation object with no project assigned # This object is used to merge all seperate translation instances later on. combined = jasy.item.Translation.TranslationItem(None, id=language) relevantLanguages = self.__expandLanguage(language) # Loop structure is build to prefer finer language matching over project priority for currentLanguage in reversed(relevantLanguages): for project in self.__projects: for translation in project.getTranslations().values(): if translation.getLanguage() == currentLanguage: Console.debug("Adding %s entries from %s @ %s...", len(translation.getTable()), currentLanguage, project.getName()) combined += translation Console.debug("Combined number of translations: %s", len(combined.getTable())) Console.outdent() self.__translationBundles[language] = combined return combined
def __addContent(self, content): Console.info("Adding manual content") Console.indent() for fileId in content: entry = content[fileId] if not isinstance(entry, dict): raise UserError("Invalid manual content section for file %s. Requires a dict with type and source definition!" % fileId) itemType = entry["type"] fileContent = entry["source"] if len(fileContent) == 0: raise UserError("Empty content!") # Support for joining text content if len(fileContent) == 1: filePath = os.path.join(self.__path, fileContent[0]) else: filePath = [os.path.join(self.__path, filePart) for filePart in fileContent] name, construct = self.__resolveConstructor(itemType) item = construct(self, fileId).attach(filePath) Console.debug("Registering %s %s" % (item.kind, fileId)) if not itemType in self.items: self.items[itemType] = {} # Check for duplication if fileId in self.items[itemType]: raise UserError("Item ID was registered before: %s" % fileId) self.items[itemType][fileId] = item Console.outdent()
def compute(tree): Console.info("Resolving variables...") Console.indent() __computeRecurser(tree, None, {}) Console.outdent()
def __build(self): """ Build static website """ self.__parseContent() self.__outputContent() Console.info("Done processing website")
def getCompressed(self, profile): """Returns the compressed CSS code of this item.""" field = "style:compressed[%s]-%s" % (self.id, profile.getId()) mtime = self.getModificationTime(profile) compressed = self.project.getCache().read(field, mtime) if compressed is None: Console.info("Compressing tree %s...", Console.colorize(self.id, "bold")) # Start with the merged tree (includes resolved) tree = self.getMergedTree(profile) # Reduce tree Engine.reduceTree(tree, profile) # Compress tree compressed = Engine.compressTree(tree, profile.getCompressionLevel(), profile.getFormattingLevel()) # Store in cache self.project.getCache().store(field, compressed, mtime) return compressed
def __getOptimizedTree(self, permutation=None, context=None): """Returns an optimized tree with permutations applied""" field = "opt-tree[%s]-%s" % (self.id, permutation) tree = self.project.getCache().read(field, self.mtime) if not tree: tree = copy.deepcopy(self.__getTree("%s:plain" % context)) # Logging msg = "Processing class %s" % Console.colorize(self.id, "bold") if permutation: msg += Console.colorize(" (%s)" % permutation, "grey") if context: msg += Console.colorize(" [%s]" % context, "cyan") Console.info("%s..." % msg) Console.indent() # Apply permutation if permutation: Console.debug("Patching tree with permutation: %s", permutation) Console.indent() jasy.js.clean.Permutate.patch(tree, permutation) Console.outdent() # Cleanups jasy.js.clean.DeadCode.cleanup(tree) ScopeScanner.scan(tree) jasy.js.clean.Unused.cleanup(tree) self.project.getCache().store(field, tree, self.mtime, True) Console.outdent() return tree
def getTranslationBundle(self, language=None): """Returns a translation object for the given language containing all relevant translation files for the current project set.""" if language is None: return None if language in self.__translationBundles: return self.__translationBundles[language] Console.info("Creating translation bundle: %s", language) Console.indent() # Initialize new Translation object with no project assigned # This object is used to merge all seperate translation instances later on. combined = jasy.item.Translation.TranslationItem(None, id=language) relevantLanguages = self.__expandLanguage(language) # Loop structure is build to prefer finer language matching over project priority for currentLanguage in reversed(relevantLanguages): for project in self.__projects: for translation in project.getTranslations().values(): if translation.getLanguage() == currentLanguage: Console.debug("Adding %s entries from %s @ %s...", len(translation.getTable()), currentLanguage, project.getName()) combined += translation Console.info("Combined number of translations: %s", len(combined.getTable())) Console.outdent() self.__translationBundles[language] = combined return combined
def loadCommands(self, objectName, fileName, encoding="utf-8"): """Loads new commands into the session wide command registry.""" counter = 0 commands = self.__commands # Method for being used as a decorator to share methods to the outside def share(func): name = "%s.%s" % (objectName, func.__name__) if name in commands: raise Exception("Command %s already exists!" % name) commands[name] = func nonlocal counter counter += 1 return func # Execute given file. Using clean new global environment # but add additional decorator for allowing to define shared methods # and the session object (self). code = open(fileName, "r", encoding=encoding).read() exec(compile(code, os.path.abspath(fileName), "exec"), {"share" : share, "session" : self}) # Export destination name as global Console.info("Imported %s.", Console.colorize("%s commands" % counter, "magenta")) return counter
def storeKernel(self, fileName, bootCode=""): Console.info("Storing kernel...") Console.indent() # Export all field data for the kernel classes = [] fieldSetupClasses = self.__session.getFieldSetupClasses() for fieldName in fieldSetupClasses: classes.append(fieldSetupClasses[fieldName]) # Transfer all hard-wired fields into a permutation self.__session.setStaticPermutation() # Sort and compress sortedClasses = self.__buildClassList(classes, bootCode, inlineTranslations=True) Console.info("Compressing %s classes...", len(sortedClasses)) compressedCode = self.__compressClasses(sortedClasses) # Write file to disk self.__fileManager.writeFile(fileName, compressedCode) # Remember kernel level classes self.__kernelClasses = sortedClasses Console.outdent()
def cleanup(node): """ """ if not hasattr(node, "variables"): ScopeScanner.scan(node) # Re cleanup until nothing to remove is found iteration = 0 cleaned = False Console.info("Removing unused variables...") Console.indent() while True: iteration += 1 modified = __cleanup(node) if modified > 0: Console.info("Removed %s unused variables", modified) ScopeScanner.scan(node) cleaned = True else: break Console.outdent() return cleaned
def __resolve(project): name = project.getName() # List of required projects Console.info("Getting requirements of %s...", Console.colorize(name, "bold")) Console.indent() requires = project.getRequires(checkoutDirectory, updateRepositories) Console.outdent() if not requires: return Console.debug("Processing %s requirements...", len(requires)) Console.indent() # Adding all project in reverse order. # Adding all local ones first before going down to their requirements for requiredProject in reversed(requires): requiredName = requiredProject.getName() if not requiredName in names: Console.debug("Adding: %s %s (via %s)", requiredName, requiredProject.version, project.getName()) names[requiredName] = True result.append(requiredProject) else: Console.debug("Blocking: %s %s (via %s)", requiredName, requiredProject.version, project.getName()) # Process all requirements of added projects for requiredProject in requires: if requiredProject.hasRequires(): __resolve(requiredProject) Console.outdent()
def removeDir(self, dirname): """Removes the given directory""" dirname = self.__session.expandFileName(dirname) if os.path.exists(dirname): Console.info("Deleting folder %s" % dirname) shutil.rmtree(dirname)
def removeFile(self, filename): """Removes the given file""" filename = self.__session.expandFileName(filename) if os.path.exists(filename): Console.info("Deleting file %s" % filename) os.remove(filename)
def processSprites(self): """Processes jasysprite files to merge sprite data into asset registry.""" assets = self.__assets configs = [fileId for fileId in assets if assets[fileId].isImageSpriteConfig()] if configs: Console.info("Processing %s...", Console.colorize("%s sprites", "magenta") % len(configs)) sprites = [] Console.indent() for fileId in configs: Console.debug("Processing %s...", fileId) asset = assets[fileId] spriteBase = os.path.dirname(fileId) try: spriteConfig = asset.getParsedObject() except ValueError as err: raise UserError("Could not parse jasysprite at %s: %s" % (fileId, err)) Console.indent() for spriteImage in spriteConfig: spriteImageId = "%s/%s" % (spriteBase, spriteImage) singleRelPaths = spriteConfig[spriteImage] Console.debug("Image %s combines %s images", spriteImageId, len(singleRelPaths)) for singleRelPath in singleRelPaths: singleId = "%s/%s" % (spriteBase, singleRelPath) singleData = singleRelPaths[singleRelPath] singleItem = assets[singleId] # Verify that sprite sheet is up-to-date fileChecksum = singleItem.getChecksum() storedChecksum = singleData["checksum"] Console.debug("Checksum Compare: %s <=> %s", fileChecksum, storedChecksum) if storedChecksum != fileChecksum: raise UserError("Sprite Sheet is not up-to-date. Checksum of %s differs." % singleId) if spriteImageId not in sprites: spriteImageIndex = len(sprites) sprites.append(spriteImageId) else: spriteImageIndex = sprites.index(spriteImageId) # Add relevant data to find image on sprite sheet singleItem.addImageSpriteData(spriteImageIndex, singleData["left"], singleData["top"]) Console.outdent() # The config file does not make any sense on the client side Console.debug("Deleting sprite config from assets: %s", fileId) del assets[fileId] Console.outdent() self.__sprites = sprites
def __init__(self, id, config, mimeTypes=None): self.id = id self.config = config self.mimeTypes = mimeTypes self.root = getKey(config, "root", ".") self.enableDebug = getKey(config, "debug", False) Console.info('Static "%s" => "%s" [debug:%s]', self.id, self.root, self.enableDebug)
def storeKernel(self, fileName, classes=None, debug=False): """ Writes a so-called kernel script to the given location. This script contains data about possible permutations based on current session values. It optionally might include asset data (useful when boot phase requires some assets) and localization data (if only one locale is built). Optimization of the script is auto-enabled when no other information is given. This method returns the classes which are included by the script so you can exclude it from the real other generated output files. """ Console.info("Storing kernel...") Console.indent() # Use a new permutation based on debug settings and statically configured fields self.__session.setStaticPermutation(debug=debug) # Build resolver # We need the permutation here because the field configuration might rely on detection classes resolver = Resolver(self.__session) detectionClasses = self.__session.getFieldDetectionClasses() for className in detectionClasses: resolver.addClassName(className) # Jasy client side classes to hold data resolver.addClassName("jasy.Env") resolver.addClassName("jasy.Asset") resolver.addClassName("jasy.Translate") # Allow kernel level mass loading of scripts (required for source, useful for build) resolver.addClassName("core.io.Script") resolver.addClassName("core.io.Queue") if classes: for className in classes: resolver.addClassName(className) # Generate boot code bootCode = "jasy.Env.setFields(%s);" % self.__session.exportFields() if self.__compressGeneratedCode: bootCode = packCode(bootCode) # Sort resulting class list sortedClasses = resolver.getSortedClasses() self.storeCompressed(sortedClasses, fileName, bootCode) # Remember classes for filtering in storeLoader/storeCompressed self.__kernelClasses = set(sortedClasses) # Reset static permutation self.__session.resetCurrentPermutation() Console.outdent()
def removeDir(self, dirname): """Removes the given directory.""" if self.__profile: dirname = self.__profile.expandFileName(dirname) if os.path.exists(dirname): Console.info("Deleting folder %s" % dirname) shutil.rmtree(dirname)
def removeFile(self, filename): """Removes the given file.""" if self.__profile: filename = self.__profile.expandFileName(filename) if os.path.exists(filename): Console.info("Deleting file %s" % filename) os.remove(filename)
def cacheManifest(profile): PREFIX = "{{prefix}}" HASH = "{{id}}" timestamp = time.strftime("%Y-%m-%d %H:%M:%S") appcache = """CACHE MANIFEST # Jasy AppCache Manifest file # Version: {version} CACHE: {htmlfile} {scripts} {assets} NETWORK: *""" htmlcache = '<!DOCTYPE html><html manifest="{manifestfile}"></html>' destinationPath = profile.getDestinationPath() fileManager = FileManager(profile) session = profile.getSession() parts = profile.getParts() assetBuilder = AssetBuilder.AssetBuilder(profile) scriptBuilder = ScriptBuilder.ScriptBuilder(profile) styleBuilder = StyleBuilder.StyleBuilder(profile) assetManager = profile.getAssetManager() htmlfile = "index.html" for permutation in profile.permutate(): scripts = [] assets = [] if KERNEL_NAME in parts: scripts.append("js/kernel.js") for part in parts: if part != KERNEL_NAME: scripts.append(profile.expandFileName("js/%s-{{id}}.js" % part)) assets.append(profile.expandFileName("css/%s-{{id}}.css" % part)) # TODO: How to get permutated asset list? for (srcFile, dstFile) in assetManager.getAssetList(): assets.append(os.path.relpath(dstFile, profile.getDestinationPath())) appcacheFilename = "appcache-{{id}}.manifest" fileManager.writeFile( "{{destination}}/" + appcacheFilename, appcache.format(version=timestamp, htmlfile=htmlfile, scripts="\n".join(scripts), assets="\n".join(assets)) ) fileManager.writeFile("{{destination}}/index-{{id}}.html", htmlcache.format(manifestfile=profile.expandFileName(appcacheFilename))) Console.info("Generate manifest file...")
def pause(self): """ Pauses the session. This release cache files etc. and makes it possible to call other jasy processes on the same projects. """ Console.info("Pausing session...") for project in self.__projects: project.pause()
def processSelectors(tree): """Processes all mixin includes inside selectors.""" Console.info("Merging mixins into selectors") Console.indent() modified = __process(tree, scanMixins=False) Console.debug("Merged %s mixins", modified) Console.outdent() return modified
def storeCompressedScript(self, items, fileName, bootCode=""): Console.info("Generating compressed script...") Console.indent() sortedScripts = self.__sortScriptItems(items, bootCode, filterBy=self.__kernelScripts, inlineTranslations=True) compressedCode = self.__compressScripts(sortedScripts) self.__fileManager.writeFile(os.path.join(self.__outputPath, fileName), compressedCode) Console.outdent()
def processExtends(tree): """Processes all requests for mixin extends.""" Console.info("Processing extend requests...") Console.indent() modified = __extend(tree) Console.debug("Processed %s selectors", modified) Console.outdent() return modified
def __generateOutput(self): """Build static website.""" Console.info("Building website....") Console.indent() self.__parseContent() self.__outputContent() Console.info("Website successfully build!")
def processMixins(tree): """Processes all mixin includes inside mixins.""" Console.info("Merging mixins with each other...") Console.indent() modified = __process(tree, scanMixins=True) Console.debug("Merged %s mixins", modified) Console.outdent() return modified
def storeLoaderScript(self, items, fileName, bootCode=""): Console.info("Generating loader script...") Console.indent() sortedScripts = self.__sortScriptItems(items, bootCode, filterBy=self.__kernelScripts) loaderCode = self.__generateScriptLoader(sortedScripts) self.__fileManager.writeFile(os.path.join(self.__outputPath, fileName), loaderCode) Console.outdent()
def clean(self): """Clears all caches of all registered projects""" Console.info("Cleaning session...") Console.indent() for project in self.__projects: project.clean() Console.outdent()
def about(): """Print outs the Jasy about page""" import jasy jasy.info() from jasy.env.Task import getCommand Console.info("Command: %s", getCommand()) Console.info("Version: %s", jasy.__version__)
def about(): """Print outs the Jasy about page.""" import jasy jasy.info() from jasy.env.Task import getCommand Console.info("Command: %s", getCommand()) Console.info("Version: %s", jasy.__version__)
def on_deleted(self, event): super(JasyEventHandler, self).on_deleted(event) what = 'directory' if event.is_directory else 'file' Console.info("Deleted %s: %s", what, event.src_path) def on_modified(self, event): super(JasyEventHandler, self).on_modified(event) what = 'directory' if event.is_directory else 'file' Console.info("Modified %s: %s", what, event.src_path)
def buildPart(self, partId, fileId): if not fileId: return Console.info("Generating style (%s)...", fileId) Console.indent() self.__profile.setWorkingPath(self.getWorkingPath()) styleItems = StyleResolver.Resolver(self.__profile).add(fileId).getSorted() self.storeCompressedStylesheet(styleItems, "%s-{{id}}.css" % partId) Console.outdent()
def mergeMixin(className, mixinName, classApi, mixinApi): Console.info("Merging %s into %s", mixinName, className) sectionLink = ["member", "property", "event"] for pos, section in enumerate(("members", "properties", "events")): mixinItems = getattr(mixinApi, section, None) if mixinItems: classItems = getattr(classApi, section, None) if not classItems: classItems = {} setattr(classApi, section, classItems) for name in mixinItems: # Overridden Check if name in classItems: # If it was included, just store another origin if "origin" in classItems[name]: classItems[name]["origin"].append({ "name": mixinName, "link": "%s:%s~%s" % (sectionLink[pos], mixinName, name) }) # Otherwise add it to the overridden list else: if not "overridden" in classItems[name]: classItems[name]["overridden"] = [] classItems[name]["overridden"].append({ "name": mixinName, "link": "%s:%s~%s" % (sectionLink[pos], mixinName, name) }) # Remember where classes are included from else: classItems[name] = {} classItems[name].update(mixinItems[name]) if not "origin" in classItems[name]: classItems[name]["origin"] = [] classItems[name]["origin"].append({ "name": mixinName, "link": "%s:%s~%s" % (sectionLink[pos], mixinName, name) })
def storeCompressedStylesheet(self, styles, fileName): Console.info("Generating compressed stylesheet...") Console.indent() # Resolve placeholders first fileName = self.__profile.expandFileName(fileName) relativeToMain = self.__session.getMain().toRelativeUrl(fileName) compressedCode = self.__compressStyles(styles) self.__fileManager.writeFile(os.path.join(self.__outputPath, fileName), compressedCode) Console.outdent()
def clean(self): """Clears all caches of all registered projects""" if not self.__projects: return Console.info("Cleaning session...") Console.indent() for project in self.__projects: project.clean() Console.outdent()
def clear(self): """Removes all generated sprite files found in the base directory.""" Console.info("Cleaning sprite files...") Console.indent() for dirPath, dirNames, fileNames in os.walk(self.base): for fileName in fileNames: if fileName.startswith("jasysprite"): filePath = os.path.join(dirPath, fileName) Console.debug("Removing file: %s", filePath) os.remove(filePath) Console.outdent()
def __init__(self, port=8080, host="127.0.0.1", mimeTypes=None): Console.info("Initializing server...") Console.indent() # Shared configuration (global/app) self.__config = { "global": { "environment": "production", "log.screen": False, "server.socket_port": port, "server.socket_host": host, "engine.autoreload.on": False, "tools.encode.on": True, "tools.encode.encoding": "utf-8" }, "/": { "log.screen": False } } self.__port = port # Build dict of content types to override native mimetype detection combinedTypes = {} combinedTypes.update(additionalContentTypes) if mimeTypes: combinedTypes.update(mimeTypes) # Update global config cherrypy.config.update(self.__config) # Somehow this screen disabling does not work # This hack to disable all access/error logging works def empty(*param, **args): pass def inspect(*param, **args): if args["severity"] > 20: Console.error("Critical error occoured:") Console.error(param[0]) cherrypy.log.access = empty cherrypy.log.error = inspect cherrypy.log.screen = False # Basic routing self.__root = Static("/", {}, mimeTypes=combinedTypes) Console.outdent()
def packDir(self, path='', recursive=True, autorotate=False, debug=False): """Pack images inside a dir into sprite sheets""" Console.info('Packing sprites in: %s' % os.path.join(self.base, path)) Console.indent() self.files = [] self.addDir(path, recursive=recursive) Console.info('Found %d images' % len(self.files)) if len(self.files) > 0: self.generate(path, autorotate, debug) Console.outdent()
def default(self, *args, **query): """ This method returns the content of existing files on the file system. Query string might be used for cache busting and are otherwise ignored. """ # Append special header to all responses cherrypy.response.headers["X-Jasy-Version"] = jasyVersion # Enable cross domain access to this server enableCrossDomain() # When it's a file name in the local folder... load it if args: path = os.path.join(*args) else: path = "index.html" path = os.path.join(self.root, path) # Check for existance first if os.path.isfile(path): if self.enableDebug: Console.info("Serving file: %s", path) # Default content type to autodetection by Python mimetype API contentType = None # Support overriding by extensions extension = os.path.splitext(path)[1] if extension: extension = extension.lower()[1:] if extension in self.mimeTypes: contentType = self.mimeTypes[ extension] + "; charset=" + locale.getpreferredencoding( ) return cherrypy.lib.static.serve_file(os.path.abspath(path), content_type=contentType) # Otherwise return a classic 404 else: if self.enableDebug: Console.warn("File at location %s not found at %s!", path, os.path.abspath(path)) raise cherrypy.NotFound(path)
def addProject(self, project): """ Adds the given project to the list of known projects. Projects should be added in order of their priority. This adds the field configuration of each project to the session fields. Fields must not conflict between different projects (same name). :param project: Instance of Project to append to the list :type project: object """ result = Project.getProjectDependencies(project, "external", self.__updateRepositories) for project in result: Console.info("Adding %s...", Console.colorize(project.getName(), "bold")) Console.indent() # Append to session list self.__projects.append(project) # Import library methods libraryPath = os.path.join(project.getPath(), "jasylibrary.py") if os.path.exists(libraryPath): self.loadLibrary(project.getName(), libraryPath, doc="Library of project %s" % project.getName()) # Import command methods commandPath = os.path.join(project.getPath(), "jasycommand.py") if os.path.exists(commandPath): self.loadCommands(project.getName(), commandPath) # Import project defined fields which might be configured using "activateField()" fields = project.getFields() for name in fields: entry = fields[name] if name in self.__fields: raise UserError("Field '%s' was already defined!" % (name)) if "check" in entry: check = entry["check"] if check in ["Boolean", "String", "Number"] or isinstance(check, list): pass else: raise UserError("Unsupported check: '%s' for field '%s'" % (check, name)) self.__fields[name] = entry Console.outdent()
def loadLibrary(self, objectName, fileName, encoding="utf-8", doc=None): """ Creates a new object inside the user API (jasyscript.py) with the given name containing all @share'd functions and fields loaded from the given file. """ if objectName in self.__scriptEnvironment: raise UserError("Could not import library %s as the object name %s is already used." % (fileName, objectName)) # Create internal class object for storing shared methods class Shared(object): pass exportedModule = Shared() exportedModule.__doc__ = doc or "Imported from %s" % os.path.relpath(fileName, os.getcwd()) counter = 0 # Method for being used as a decorator to share methods to the outside def share(func): nonlocal counter setattr(exportedModule, func.__name__, func) counter += 1 return func def itemtype(type, name): def wrap(cls): id = "%s.%s" % (objectName, type[0].upper() + type[1:]) self.addItemType(id, name, cls) return cls return wrap def postscan(): def wrap(f): self.__postscans.append(f) return f return wrap # Execute given file. Using clean new global environment # but add additional decorator for allowing to define shared methods # and the session object (self). code = open(fileName, "r", encoding=encoding).read() exec(compile(code, os.path.abspath(fileName), "exec"), {"share" : share, "itemtype": itemtype, "postscan": postscan, "session" : self}) # Export destination name as global self.__scriptEnvironment[objectName] = exportedModule Console.info("Imported %s.", Console.colorize("%s methods" % counter, "magenta")) return counter
def __getTree(self): """Returns the abstract syntax tree of the stylesheet.""" field = "style:tree[%s]" % self.id tree = self.project.getCache().read(field, self.mtime) if not tree: Console.info("Parsing stylesheet %s...", Console.colorize(self.id, "bold")) Console.indent() tree = Engine.getTree(self.getText(), self.id) Console.outdent() self.project.getCache().store(field, tree, self.mtime, True) return tree
def __getTree(self, context=None): field = "tree[%s]" % self.id tree = self.project.getCache().read(field, self.mtime) if not tree: Console.info("Processing class %s %s...", Console.colorize(self.id, "bold"), Console.colorize("[%s]" % context, "cyan")) Console.indent() tree = Parser.parse(self.getText(), self.id) ScopeScanner.scan(tree) Console.outdent() self.project.getCache().store(field, tree, self.mtime, True) return tree
def __getTree(self): """Returns the abstract syntax tree.""" field = "script:tree[%s]" % self.id tree = self.project.getCache().read(field, self.mtime) if not tree: Console.info("Processing class %s...", Console.colorize(self.id, "bold")) Console.indent() tree = Parser.parse(self.getText(), self.id) ScopeScanner.scan(tree) Console.outdent() self.project.getCache().store(field, tree, self.mtime, True) return tree