Exemple #1
0
    def __init__(self, id, config):
        self.id = id
        self.config = config
        self.host = getKey(config, "host")
        self.auth = getKey(config, "auth")
        
        self.enableDebug = getKey(config, "debug", False)
        self.enableMirror = getKey(config, "mirror", False)
        self.enableOffline = getKey(config, "offline", False)

        if self.enableMirror:
            self.mirror = Cache(os.getcwd(), "jasymirror-%s" % self.id, hashkeys=True)

        info('Proxy "%s" => "%s" [debug:%s|mirror:%s|offline:%s]', self.id, self.host, self.enableDebug, self.enableMirror, self.enableOffline)
Exemple #2
0
    def __init__(self, path, config=None, version=None):
        """
        Constructor call of the project. 

        - First param is the path of the project relative to the current working directory.
        - Config can be read from jasyproject.json or using constructor parameter @config
        - Parent is used for structural debug messages (dependency trees)
        """
        
        if not os.path.isdir(path):
            raise JasyError("Invalid project path: %s" % path)
        
        # Only store and work with full path
        self.__path = os.path.abspath(os.path.expanduser(path))
        
        # Store given params
        self.version = version
        
        # Intialize item registries
        self.classes = {}
        self.assets = {}        
        self.docs = {}
        self.translations = {}

        # Load project configuration
        self.__config = Config(config)
        self.__config.loadValues(os.path.join(self.__path, "jasyproject"), optional=True)

        # Initialize cache
        try:
            self.__cache = Cache(self.__path)
        except IOError as err:
            raise JasyError("Could not initialize project. Cache file in %s could not be initialized! %s" % (self.__path, err))
        
        # Read name from manifest or use the basename of the project's path
        self.__name = self.__config.get("name", getProjectNameFromPath(self.__path))
            
        # Read requires
        self.__requires = self.__config.get("requires", {})
        
        # Defined whenever no package is defined and classes/assets are not stored in the toplevel structure.
        self.__package = self.__config.get("package", self.__name if self.__config.has("name") else None)

        # Read fields (for injecting data into the project and build permutations)
        self.__fields = self.__config.get("fields", {})
Exemple #3
0
    def __init__(self, path):
        """
        Constructor call of the project. 
        First param is the path of the project relative to the current working directory.
        """
        
        path = os.path.normpath(path)
        if not os.path.isdir(path):
            raise JasyError("Invalid project path: %s (Absolute: %s)" % (path, os.path.abspath(path)))
        
        # Only store and work with full path
        self.__path = os.path.abspath(path)


        # Initialize cache
        try:
            self.__cache = Cache(self.__path)
        except IOError as err:
            raise JasyError("Could not initialize project. Cache file could not be initialized! %s" % err)
        
        
        # Load project configuration
        projectConfigPath = os.path.join(self.__path, "jasyproject.json")
        if not os.path.exists(projectConfigPath):
            raise JasyError("Missing jasyproject.json at: %s" % projectConfigPath)
        
        # Parse project configuration
        try:
            projectData = json.load(open(projectConfigPath))
        except ValueError as err:
            raise JasyError("Could not parse jasyproject.json at %s: %s" % (projectConfigPath, err))
        
        
        # Read name from manifest or use the basename of the project's path
        if "name" in projectData:
            self.__name = projectData["name"]
        else:
            self.__name = os.path.basename(self.__path)
            
        # Defined whenever no package is defined and classes/assets are not stored in the toplevel structure.
        if "package" in projectData:
            self.__package = projectData["package"]
        else:
            self.__package = self.__name

        # Whether we need to parse files for get their correct name (using @name attributes)
        if "fuzzy" in projectData:
            self.__fuzzy = projectData["fuzzy"]
        else:
            self.__fuzzy = False

        # Read fields (for injecting data into the project and build permuations)
        if "fields" in projectData:
            self.__fields = projectData["fields"]
        else:
            self.__fields = {}            
            

        # Try to figure out folder structure automatically
        if os.path.isdir(os.path.join(self.__path, "source", "class")):
            self.__classPath = os.path.join("source", "class")
            self.__assetPath = os.path.join("source", "asset")
            self.__translationPath = os.path.join("source", "translation")
        elif os.path.isdir(os.path.join(self.__path, "class")):
            self.__classPath = "class"
            self.__assetPath = "asset"
            self.__translationPath = "translation"
        elif os.path.isdir(os.path.join(self.__path, "src")):
            self.__classPath = "src"
            self.__assetPath = "src"
            self.__translationPath = "src"
        else:
            self.__classPath = ""
            self.__assetPath = ""
            self.__translationPath = ""

        logging.info("Initialized project %s" % self.__name)
Exemple #4
0
class Project():
    
    def __init__(self, path):
        """
        Constructor call of the project. 
        First param is the path of the project relative to the current working directory.
        """
        
        path = os.path.normpath(path)
        if not os.path.isdir(path):
            raise JasyError("Invalid project path: %s (Absolute: %s)" % (path, os.path.abspath(path)))
        
        # Only store and work with full path
        self.__path = os.path.abspath(path)


        # Initialize cache
        try:
            self.__cache = Cache(self.__path)
        except IOError as err:
            raise JasyError("Could not initialize project. Cache file could not be initialized! %s" % err)
        
        
        # Load project configuration
        projectConfigPath = os.path.join(self.__path, "jasyproject.json")
        if not os.path.exists(projectConfigPath):
            raise JasyError("Missing jasyproject.json at: %s" % projectConfigPath)
        
        # Parse project configuration
        try:
            projectData = json.load(open(projectConfigPath))
        except ValueError as err:
            raise JasyError("Could not parse jasyproject.json at %s: %s" % (projectConfigPath, err))
        
        
        # Read name from manifest or use the basename of the project's path
        if "name" in projectData:
            self.__name = projectData["name"]
        else:
            self.__name = os.path.basename(self.__path)
            
        # Defined whenever no package is defined and classes/assets are not stored in the toplevel structure.
        if "package" in projectData:
            self.__package = projectData["package"]
        else:
            self.__package = self.__name

        # Whether we need to parse files for get their correct name (using @name attributes)
        if "fuzzy" in projectData:
            self.__fuzzy = projectData["fuzzy"]
        else:
            self.__fuzzy = False

        # Read fields (for injecting data into the project and build permuations)
        if "fields" in projectData:
            self.__fields = projectData["fields"]
        else:
            self.__fields = {}            
            

        # Try to figure out folder structure automatically
        if os.path.isdir(os.path.join(self.__path, "source", "class")):
            self.__classPath = os.path.join("source", "class")
            self.__assetPath = os.path.join("source", "asset")
            self.__translationPath = os.path.join("source", "translation")
        elif os.path.isdir(os.path.join(self.__path, "class")):
            self.__classPath = "class"
            self.__assetPath = "asset"
            self.__translationPath = "translation"
        elif os.path.isdir(os.path.join(self.__path, "src")):
            self.__classPath = "src"
            self.__assetPath = "src"
            self.__translationPath = "src"
        else:
            self.__classPath = ""
            self.__assetPath = ""
            self.__translationPath = ""

        logging.info("Initialized project %s" % self.__name)


    __dirFilter = [".svn", ".git", ".hg", ".bzr"]
    __internalFiles = ("jasyproject.json", "jasyscript.py", "jasycache", "jasycache.db"),


    def __str__(self):
        return self.__path

    
    def getName(self):
        return self.__name

    
    def getPath(self):
        return self.__path

    
    def getPackage(self):
        return self.__package
        
    
    def isFuzzy(self):
        return self.__fuzzy
        
        
    def getCache(self):
        return self.__cache
    
    
    def clearCache(self):
        self.__cache.clear()
        
        
    def close(self):
        self.__cache.close()
        
        
    def getFields(self):
        """
        Return the project defined fields which may be configured by the build script
        """
        
        return self.__fields
        
        
    def getClassByName(self, className):
        """
        Finds a class by its name.
        """
        
        try:
            return self.getClasses()[className]
        except KeyError:
            return None         
        
    
    def getClassPath(self, relative=False):
        """
        Returns the full path to the JavaScript classes
        """

        if self.__classPath is None:
            return None

        return self.__classPath if relative else os.path.join(self.__path, self.__classPath)


    def getAssetPath(self, relative=False):
        """
        Returns the full path to the assets
        """

        if self.__assetPath is None:
            return None

        return self.__assetPath if relative else os.path.join(self.__path, self.__assetPath)


    def getTranslationPath(self, relative=False):
        """
        Returns the full path to the translation files
        """
        
        if self.__translationPath is None:
            return None
        
        return self.__translationPath if relative else os.path.join(self.__path, self.__translationPath)


    def getClasses(self):
        """ 
        Returns all project JavaScript classes. Requires all files to have a "js" extension.
        """
        
        if self.__classPath is None:
            return None
        
        try:
            return self.classes
            
        except AttributeError:
            classPath = os.path.join(self.__path, self.__classPath)
            classes = {}
            
            if classPath and os.path.exists(classPath):
                for dirPath, dirNames, fileNames in os.walk(classPath):
                    for dirName in dirNames:
                        if dirName in self.__dirFilter:
                            dirNames.remove(dirName)

                    for fileName in fileNames:
                        if fileName[0] == ".":
                            continue
                        
                        if fileName.endswith(".js"):
                            classObj = Class(os.path.join(dirPath, fileName), self)
                            className = classObj.getName()
                            
                            if className in classes:
                                raise Exception("Class duplication detected: %s and %s" % (classObj.getPath(), classes[className].getPath()))
                                
                            classes[className] = classObj
                
            logging.debug("Project %s contains %s classes", self.__name, len(classes))
            self.classes = classes
            return classes


    def getAssets(self):
        """ 
        Returns all project asssets (images, stylesheets, static data, etc.). Does not filter
        for specific extensions but ignores files starting with a dot or files used internally
        by Jasy like cache files or project configuration.
        """
        
        if self.__assetPath is None:
            return None
        
        try:
            return self.assets
            
        except AttributeError:
            assetPath = os.path.join(self.__path, self.__assetPath)
            assets = {}
            package = self.__package

            if assetPath and os.path.exists(assetPath):
                assetPathLen = len(assetPath) + 1
                for dirPath, dirNames, fileNames in os.walk(assetPath):
                    for dirName in dirNames:
                        if dirName in self.__dirFilter:
                            dirNames.remove(dirName)

                    for fileName in fileNames:
                        if fileName[0] == "." or (fileName.endswith(".js") and self.__assetPath == self.__classPath):
                            continue
                            
                        # Exclude internally managed files
                        if fileName in self.__internalFiles:
                            continue
                            
                        filePath = os.path.join(dirPath, fileName)
                        relPath = filePath[assetPathLen:]
                        
                        # Support for pre-fixed package which is not used in filesystem, but in assets
                        if package:
                            name = "%s%s%s" % (package, os.sep, relPath)
                        else:
                            name = relPath
                            
                        # always using unix paths for the asset ID
                        assets[name.replace(os.sep, "/")] = filePath
                    
            logging.debug("Project %s contains %s assets", self.__name, len(assets))
            self.assets = assets
            return assets


    def getTranslations(self):
        """ 
        Returns all translation files. Currently supports only gettext style PO files
        with a "po" extension.
        """
        
        if self.__translationPath is None:
            return None
        
        try:
            return self.translations
            
        except AttributeError:
            translationPath = os.path.join(self.__path, self.__translationPath)
            translations = {}

            if translationPath and os.path.exists(translationPath):
                for dirPath, dirNames, fileNames in os.walk(translationPath):
                    for dirName in dirNames:
                        if dirName in self.__dirFilter:
                            dirNames.remove(dirName)

                    for fileName in fileNames:    
                        if fileName[0] == "." or not fileName.endswith(".po"):
                            continue

                        translations[os.path.splitext(fileName)[0]] = os.path.join(dirPath, fileName)
            
            logging.debug("Project %s contains %s translations", self.__name, len(translations))
            self.translations = translations
            return translations
Exemple #5
0
    def __init__(self, path, config=None, version=None, repo=None, revision=None):
        """
        Constructor call of the project. 

        - First param is the path of the project relative to the current working directory.
        - Config can be read from jasyproject.json or using constructor parameter @config
        - Parent is used for structural debug messages (dependency trees)
        """
        
        if not os.path.isdir(path):
            raise JasyError("Invalid project path: %s" % path)
        
        # Only store and work with full path
        self.__path = os.path.abspath(os.path.expanduser(path))
        
        # Store given params
        self.__version = version
        self.__repo = repo
        self.__revision = revision
        
        # Intialize item registries
        self.classes = {}
        self.assets = {}        
        self.docs = {}
        self.translations = {}

        # Load project configuration
        configFilePath = os.path.join(self.__path, "jasyproject.json")
        isJasyProject = os.path.exists(configFilePath)
        if isJasyProject:
            try:
                storedConfig = json.load(open(configFilePath))
            except ValueError as err:
                raise JasyError("Could not parse jasyproject.json at %s: %s" % (configFilePath, err))
                
            if config:
                for key in storedConfig:
                    if not key in config:
                        config[key] = storedConfig[key]
            else:
                config = storedConfig
                
        if config is None:
            raise JasyError("Could not initialize project configuration in %s!" % self.__path)
            
        # Initialize cache
        try:
            self.__cache = Cache(self.__path)
        except IOError as err:
            raise JasyError("Could not initialize project. Cache file could not be initialized! %s" % err)
        
        # Read name from manifest or use the basename of the project's path
        self.__name = getKey(config, "name", getProjectNameFromPath(self.__path))
            
        # Read requires
        self.__requires = getKey(config, "requires", {})
        
        # Defined whenever no package is defined and classes/assets are not stored in the toplevel structure.
        self.__package = getKey(config, "package", self.__name if isJasyProject else None)

        # Read fields (for injecting data into the project and build permuations)
        self.__fields = getKey(config, "fields", {})

        # Store config
        self.__config = config
        
        # This section is a must for non jasy projects
        if not "content" in config and not isJasyProject:
            raise JasyError("Missing 'content' section for compat project!")
Exemple #6
0
class Project():
    
    kind = "none"
    
    def __init__(self, path, config=None, version=None, repo=None, revision=None):
        """
        Constructor call of the project. 

        - First param is the path of the project relative to the current working directory.
        - Config can be read from jasyproject.json or using constructor parameter @config
        - Parent is used for structural debug messages (dependency trees)
        """
        
        if not os.path.isdir(path):
            raise JasyError("Invalid project path: %s" % path)
        
        # Only store and work with full path
        self.__path = os.path.abspath(os.path.expanduser(path))
        
        # Store given params
        self.__version = version
        self.__repo = repo
        self.__revision = revision
        
        # Intialize item registries
        self.classes = {}
        self.assets = {}        
        self.docs = {}
        self.translations = {}

        # Load project configuration
        configFilePath = os.path.join(self.__path, "jasyproject.json")
        isJasyProject = os.path.exists(configFilePath)
        if isJasyProject:
            try:
                storedConfig = json.load(open(configFilePath))
            except ValueError as err:
                raise JasyError("Could not parse jasyproject.json at %s: %s" % (configFilePath, err))
                
            if config:
                for key in storedConfig:
                    if not key in config:
                        config[key] = storedConfig[key]
            else:
                config = storedConfig
                
        if config is None:
            raise JasyError("Could not initialize project configuration in %s!" % self.__path)
            
        # Initialize cache
        try:
            self.__cache = Cache(self.__path)
        except IOError as err:
            raise JasyError("Could not initialize project. Cache file could not be initialized! %s" % err)
        
        # Read name from manifest or use the basename of the project's path
        self.__name = getKey(config, "name", getProjectNameFromPath(self.__path))
            
        # Read requires
        self.__requires = getKey(config, "requires", {})
        
        # Defined whenever no package is defined and classes/assets are not stored in the toplevel structure.
        self.__package = getKey(config, "package", self.__name if isJasyProject else None)

        # Read fields (for injecting data into the project and build permuations)
        self.__fields = getKey(config, "fields", {})

        # Store config
        self.__config = config
        
        # This section is a must for non jasy projects
        if not "content" in config and not isJasyProject:
            raise JasyError("Missing 'content' section for compat project!")


    #
    # Project Scan/Init
    #
    
    def init(self):
        
        config = self.__config
            
        # Processing custom content section. Only supports classes and assets.
        if "content" in config:
            self.__kind = "manual"
            self.__addContent(config["content"])

        # Application projects
        elif self.__hasDir("source"):
            self.__kind = "application"

            if self.__hasDir("source/class"):
                self.__addDir("source/class", "classes")
            if self.__hasDir("source/asset"):
                self.__addDir("source/asset", "assets")
            if self.__hasDir("source/translation"):
                self.__addDir("source/translation", "translations")
                
        # Compat - please change to class/style/asset instead
        elif self.__hasDir("src"):
            self.__kind = "resource"
            self.__addDir("src", "classes")

        # Resource projects
        else:
            self.__kind = "resource"

            if self.__hasDir("class"):
                self.__addDir("class", "classes")
            if self.__hasDir("asset"):
                self.__addDir("asset", "assets")
            if self.__hasDir("translation"):
                self.__addDir("translation", "translations")

        # Generate summary
        summary = []
        for section in ["classes", "assets", "translations"]:
            content = getattr(self, section, None)
            if content:
                summary.append("%s %s" % (len(content), section))

        if summary:
            msg = "%s " % colorize(self.getName(), "bold")
            
            if self.__version:
                msg += "@ %s" % colorize(self.__version, "magenta")

                rev = self.__revision
                if rev is not None:
                    if type(rev) is str and len(rev) > 10:
                        rev = rev[:6]
                    msg += colorize("-%s " % rev, "grey")
                    
                else:
                    msg += " "
                    
            msg += "[%s]: %s" % (colorize(self.__kind, "cyan"), colorize(", ".join(summary), "grey"))
            
            info(msg)
                
        else:
            error("Project %s is empty!", self.getName())





    #
    # FILE SYSTEM INDEXER
    #
    
    def __hasDir(self, directory):
        full = os.path.join(self.__path, directory)
        if os.path.exists(full):
            if not os.path.isdir(full):
                raise JasyError("Expecting %s to be a directory: %s" % full)
            
            return True
        
        return False
        
        
    def __addContent(self, content):
        debug("Adding manual content")
        
        indent()
        for fileId in content:
            fileContent = content[fileId]
            if len(fileContent) == 0:
                raise JasyError("Empty content!")
                
            # If the user defines a file extension for JS public idenfiers 
            # (which is not required) we filter them out
            if fileId.endswith(".js"):
                raise JasyError("JavaScript files should define the exported name, not a file name: %s" % fileId)

            fileExtension = os.path.splitext(fileContent[0])[1]
            
            # 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]
            
            # Structure files
            if fileExtension in classExtensions:
                construct = Class
                dist = self.classes
            elif fileExtension in translationExtensions:
                construct = Translation
                dist = self.translations
            else:
                construct = Asset
                dist = self.assets
                
            # Check for duplication
            if fileId in dist:
                raise JasyError("Item ID was registered before: %s" % fileId)
            
            # Create instance
            item = construct(self, fileId).attach(filePath)
            debug("Registering %s %s" % (item.kind, fileId))
            dist[fileId] = item
            
        outdent()
        
        
    def __addDir(self, directory, distname):
        
        debug("Scanning directory: %s" % directory)
        indent()
        
        path = os.path.join(self.__path, directory)
        if not os.path.exists(path):
            return
            
        for dirPath, dirNames, fileNames in os.walk(path):
            for dirName in dirNames:
                # Filter dotted directories like .git, .bzr, .hg, .svn, etc.
                if dirName.startswith("."):
                    dirNames.remove(dirName)

                # Filter sub projects
                if os.path.exists(os.path.join(dirPath, dirName, "jasyproject.json")):
                    dirNames.remove(dirName)
                    
            relDirPath = os.path.relpath(dirPath, path)

            for fileName in fileNames:
                
                if fileName[0] == ".":
                    continue
                    
                relPath = os.path.normpath(os.path.join(relDirPath, fileName)).replace(os.sep, "/")
                fullPath = os.path.join(dirPath, fileName)
                
                self.addFile(relPath, fullPath, distname)
        
        outdent()


    def addFile(self, relPath, fullPath, distname, override=False):
        
        fileName = os.path.basename(relPath)
        fileExtension = os.path.splitext(fileName)[1]

        # Prepand package
        if self.__package:
            fileId = "%s/" % self.__package
        else:
            fileId = ""

        # Structure files  
        if fileExtension in classExtensions and distname == "classes":
            fileId += os.path.splitext(relPath)[0]
            construct = Class
            dist = self.classes
        elif fileExtension in translationExtensions and distname == "translations":
            fileId += os.path.splitext(relPath)[0]
            construct = Translation
            dist = self.translations
        elif fileName in docFiles:
            fileId += os.path.dirname(relPath)
            fileId = fileId.strip("/") # edge case when top level directory
            construct = Doc
            dist = self.docs
        else:
            fileId += relPath
            construct = Asset
            dist = self.assets
            
        # Only assets keep unix style paths identifiers
        if construct != Asset:
            fileId = fileId.replace("/", ".")

        # Check for duplication
        if fileId in dist and not override:
            raise JasyError("Item ID was registered before: %s" % fileId)

        # Create instance
        item = construct(self, fileId).attach(fullPath)
        debug("Registering %s %s" % (item.kind, fileId))
        dist[fileId] = item
        
        
    

    #
    # ESSENTIALS
    #
    
    def getRequires(self, prefix="external"):
        """
        Return the project requirements as project instances
        """

        result = []
        
        for entry in self.__requires:
            repo = None
            revision = None
            
            if type(entry) is dict:
                source = entry["source"]
                config = getKey(entry, "config")
                version = getKey(entry, "version")
            else:
                source = entry
                config = None
                version = None
            
            if version:
                info("Processing: %s @ %s", source, version)
            else:
                info("Processing: %s", source)
                
            indent()
            
            if isGitRepositoryUrl(source):
                if not version:
                    version = "master"

                # Auto cloning always happens relative to main project root folder (not to project requiring it)
                retval = cloneGit(source, version, prefix=prefix)
                if not retval:
                    raise JasyError("Could not clone GIT repository %s" % source)
                    
                path, revision = retval
                path = os.path.abspath(path)
                repo = "git"
                
            else:
                if not source.startswith(("/", "~")):
                    path = os.path.join(self.__path, source)
                else:
                    path = source
                
                # Other references to requires projects are always relative to the project requiring it
                path = os.path.normpath(os.path.expanduser(path))
                repo = "local"
                
            project = getProjectFromPath(path, config, version, repo, revision)
            result.append(project)
            
            outdent()
            
        return result


    def getFields(self):
        """ Return the project defined fields which may be configured by the build script """
        return self.__fields


    def getClassByName(self, className):
        """ Finds a class by its name."""

        try:
            return self.getClasses()[className]
        except KeyError:
            return None

    def getName(self):
        return self.__name
    
    def getPath(self):
        return self.__path
    
    def getPackage(self):
        return self.__package
        
    def toRelativeUrl(self, path, prefix="", subpath="source"):
        root = os.path.join(self.__path, subpath)
        relpath = os.path.relpath(path, root)

        if prefix:
            if not prefix[-1] == os.sep:
                prefix += os.sep
                
            relpath = os.path.normpath(prefix + relpath)
            
        return relpath.replace(os.sep, "/")



    #
    # CACHE API
    #
    
    def getCache(self):
        """Returns the cache instance"""
        
        return self.__cache
    
    def clean(self):
        """Clears the cache of the project"""
        
        info("Clearing cache of %s..." % self.__name)
        self.__cache.clear()
        
    def close(self):
        """Closes the project which deletes the internal caches"""
        
        self.__cache.close()
        self.__cache = None
        
        self.classes = None
        self.assets = None
        self.docs = None
        self.translations = None
        
    def pause(self):
        """Pauses the project so that other processes could modify/access it"""
        
        self.__cache.close()
        
    def resume(self):
        """Resumes the paused project"""
        
        self.__cache.open()



    #
    # LIST ACCESSORS
    #
    
    def getDocs(self):
        """Returns all package docs"""
        return self.docs

    def getClasses(self):
        """ Returns all project JavaScript classes. Requires all files to have a "js" extension. """
        return self.classes

    def getAssets(self):
        """ Returns all project asssets (images, stylesheets, static data, etc.). """
        return self.assets

    def getTranslations(self):
        """ Returns all translation files. Supports gettext style PO files with .po extension. """
        return self.translations

        
Exemple #7
0
class Proxy(object):
    
    def __init__(self, id, config):
        self.id = id
        self.config = config
        self.host = getKey(config, "host")
        self.auth = getKey(config, "auth")
        
        self.enableDebug = getKey(config, "debug", False)
        self.enableMirror = getKey(config, "mirror", False)
        self.enableOffline = getKey(config, "offline", False)

        if self.enableMirror:
            self.mirror = Cache(os.getcwd(), "jasymirror-%s" % self.id, hashkeys=True)

        info('Proxy "%s" => "%s" [debug:%s|mirror:%s|offline:%s]', self.id, self.host, self.enableDebug, self.enableMirror, self.enableOffline)
        
        
    # These headers will be blocked between header copies
    __blockHeaders = CaseInsensitiveDict.fromkeys([
        "content-encoding", 
        "content-length", 
        "connection", 
        "keep-alive", 
        "proxy-authenticate", 
        "proxy-authorization", 
        "transfer-encoding", 
        "remote-addr", 
        "host"
    ])
    
    
    @cherrypy.expose
    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.
        """
        
        url = self.config["host"] + "/".join(args)
        result = None

        # Try using offline mirror if feasible
        if self.enableMirror and cherrypy.request.method == "GET":
            mirrorId = "%s[%s]" % (url, json.dumps(query, separators=(',',':'), sort_keys=True))
            result = self.mirror.read(mirrorId)
            if result is not None and self.enableDebug:
                info("Mirrored: %s" % url)
         
        # Check if we're in forced offline mode
        if self.enableOffline and result is None:
            info("Offline: %s" % url)
            raise cherrypy.NotFound(url)
        
        # Load URL from remote server
        if result is None:

            # Prepare headers
            headers = CaseInsensitiveDict()
            for name in cherrypy.request.headers:
                if not name in self.__blockHeaders:
                    headers[name] = cherrypy.request.headers[name]
            
            # Load URL from remote host
            try:
                if self.enableDebug:
                    info("Requesting: %s", url)
                    
                # Apply headers for basic HTTP authentification
                if "X-Proxy-Authorization" in headers:
                    headers["Authorization"] = headers["X-Proxy-Authorization"]
                    del headers["X-Proxy-Authorization"]                
                    
                # Add headers for different authentification approaches
                if self.auth:
                    
                    # Basic Auth
                    if self.auth["method"] == "basic":
                        headers["Authorization"] = b"Basic " + base64.b64encode(("%s:%s" % (self.auth["user"], self.auth["password"])).encode("ascii"))
                    
                # We disable verifícation of SSL certificates to be more tolerant on test servers
                result = requests.get(url, params=query, headers=headers, verify=False)
                
            except Exception as err:
                if self.enableDebug:
                    info("Request failed: %s", err)
                    
                raise cherrypy.HTTPError(403)

            # Storing result into mirror
            if self.enableMirror and cherrypy.request.method == "GET":

                # Wrap result into mirrorable entry
                resultCopy = Result(result.headers, result.content)
                self.mirror.store(mirrorId, resultCopy)
        

        # Copy response headers to our reponse
        for name in result.headers:
            if not name.lower() in self.__blockHeaders:
                cherrypy.response.headers[name] = result.headers[name]

        # Append special header to all responses
        cherrypy.response.headers["X-Jasy-Version"] = jasy.__version__
        
        # Enable cross domain access to this server
        enableCrossDomain()

        return result.content
Exemple #8
0
    def __init__(self, path):
        path = os.path.normpath(path)

        if not os.path.isdir(path):
            raise ProjectException("Invalid project path: %s (Absolute: %s)" % (path, os.path.abspath(path)))
        
        # Only store and work with full path
        path = os.path.abspath(path)
        
        self.__path = path
        self.__dirFilter = [".svn",".git",".hg",".bzr"]

        try:
            self.__cache = Cache(self.__path)
        except IOError as err:
            raise ProjectException("Could not initialize project. Cache file could not be initialized! %s" % err)
        
        manifestPath = os.path.join(path, "manifest.json")
        if not os.path.exists(manifestPath):
            raise ProjectException("Missing manifest.json at: %s" % manifestPath)
        
        try:
            manifestData = json.load(open(manifestPath))
        except ValueError as err:
            raise ProjectException("Could not parse manifest.json at %s: %s" % (manifestPath, err))
        
        # Read name from manifest or use the basename of the project's path
        if "name" in manifestData:
            self.__name = manifestData["name"]
        else:
            self.__name = os.path.basename(path)
            
        # Detect kind automatically
        if "kind" in manifestData:
            self.__kind = manifestData["kind"]
        elif os.path.isdir(os.path.join(self.__path, "source", "class")):
            self.__kind = "full"
        elif os.path.isdir(os.path.join(self.__path, "class")):
            self.__kind = "basic"
        elif os.path.isdir(os.path.join(self.__path, "src")):
            self.__kind = "classic"
        else:
            self.__kind = "flat"
                
        # Defined whenever no package is defined and classes/assets are not stored in the toplevel structure.
        if "package" in manifestData:
            self.__package = manifestData["package"]
        else:
            self.__package = self.__name
        
        # Whether we need to parse files for get their correct name (using @name attributes)
        if "fuzzy" in manifestData:
            self.__fuzzy = manifestData["fuzzy"]
        else:
            self.__fuzzy = False
            
        # Read fields (for injecting data into the project and build permuations)
        if "fields" in manifestData:
            self.__fields = manifestData["fields"]
        else:
            self.__fields = {}

        logging.info("Initialized project %s (%s)" % (self.__name, self.__kind))

        # Do kind specific intialization
        if self.__kind == "full":
            self.__classPath = os.path.join("source", "class")
            self.__assetPath = os.path.join("source", "asset")
            self.__translationPath = os.path.join("source", "translation")
        elif self.__kind == "basic":
            self.__classPath = "class"
            self.__assetPath = "asset"
            self.__translationPath = "translation"
        elif self.__kind == "classic":
            self.__classPath = "src"
            self.__assetPath = "src"
            self.__translationPath = None
        elif self.__kind == "flat":
            self.__classPath = ""
            self.__assetPath = ""
            self.__translationPath = None
        else:
            raise ProjectException("Unsupported kind of project: %s" % self.__kind)
Exemple #9
0
class Project():
    def __init__(self, path):
        path = os.path.normpath(path)

        if not os.path.isdir(path):
            raise ProjectException("Invalid project path: %s (Absolute: %s)" % (path, os.path.abspath(path)))
        
        # Only store and work with full path
        path = os.path.abspath(path)
        
        self.__path = path
        self.__dirFilter = [".svn",".git",".hg",".bzr"]

        try:
            self.__cache = Cache(self.__path)
        except IOError as err:
            raise ProjectException("Could not initialize project. Cache file could not be initialized! %s" % err)
        
        manifestPath = os.path.join(path, "manifest.json")
        if not os.path.exists(manifestPath):
            raise ProjectException("Missing manifest.json at: %s" % manifestPath)
        
        try:
            manifestData = json.load(open(manifestPath))
        except ValueError as err:
            raise ProjectException("Could not parse manifest.json at %s: %s" % (manifestPath, err))
        
        # Read name from manifest or use the basename of the project's path
        if "name" in manifestData:
            self.__name = manifestData["name"]
        else:
            self.__name = os.path.basename(path)
            
        # Detect kind automatically
        if "kind" in manifestData:
            self.__kind = manifestData["kind"]
        elif os.path.isdir(os.path.join(self.__path, "source", "class")):
            self.__kind = "full"
        elif os.path.isdir(os.path.join(self.__path, "class")):
            self.__kind = "basic"
        elif os.path.isdir(os.path.join(self.__path, "src")):
            self.__kind = "classic"
        else:
            self.__kind = "flat"
                
        # Defined whenever no package is defined and classes/assets are not stored in the toplevel structure.
        if "package" in manifestData:
            self.__package = manifestData["package"]
        else:
            self.__package = self.__name
        
        # Whether we need to parse files for get their correct name (using @name attributes)
        if "fuzzy" in manifestData:
            self.__fuzzy = manifestData["fuzzy"]
        else:
            self.__fuzzy = False
            
        # Read fields (for injecting data into the project and build permuations)
        if "fields" in manifestData:
            self.__fields = manifestData["fields"]
        else:
            self.__fields = {}

        logging.info("Initialized project %s (%s)" % (self.__name, self.__kind))

        # Do kind specific intialization
        if self.__kind == "full":
            self.__classPath = os.path.join("source", "class")
            self.__assetPath = os.path.join("source", "asset")
            self.__translationPath = os.path.join("source", "translation")
        elif self.__kind == "basic":
            self.__classPath = "class"
            self.__assetPath = "asset"
            self.__translationPath = "translation"
        elif self.__kind == "classic":
            self.__classPath = "src"
            self.__assetPath = "src"
            self.__translationPath = None
        elif self.__kind == "flat":
            self.__classPath = ""
            self.__assetPath = ""
            self.__translationPath = None
        else:
            raise ProjectException("Unsupported kind of project: %s" % self.__kind)
    
    
    def __str__(self):
        return self.__path

    
    def getName(self):
        return self.__name

    
    def getPath(self):
        return self.__path

    
    def getPackage(self):
        return self.__package
        
    
    def isFuzzy(self):
        return self.__fuzzy
        
        
    def getCache(self):
        return self.__cache
    
    
    def clearCache(self):
        self.__cache.clear()
        
        
    def close(self):
        self.__cache.close()
        
        
    def getFields(self):
        """ Return the project defined fields which may be configured by the build script """
        return self.__fields
        
        
    def getClassByName(self, className):
        try:
            return self.getClasses()[className]
        except KeyError:
            return None         
        
    
    def getClassPath(self, relative=False):
        """ Returns the full path to the JavaScript classes """

        if self.__classPath is None:
            return None

        return self.__classPath if relative else os.path.join(self.__path, self.__classPath)

    def getAssetPath(self, relative=False):
        """ Returns the full path to the assets (images, stylesheets, etc.) """

        if self.__assetPath is None:
            return None

        return self.__assetPath if relative else os.path.join(self.__path, self.__assetPath)

    def getTranslationPath(self, relative=False):
        """ Returns the full path to the translation files (gettext *.po files) """
        
        if self.__translationPath is None:
            return None
        
        return self.__translationPath if relative else os.path.join(self.__path, self.__translationPath)


    def getClasses(self):
        """ Returns all project JavaScript classes """
        
        if self.__classPath is None:
            return None
        
        try:
            return self.classes
            
        except AttributeError:
            classPath = os.path.join(self.__path, self.__classPath)
            classes = {}
            
            if classPath and os.path.exists(classPath):
                for dirPath, dirNames, fileNames in os.walk(classPath):
                    for dirName in dirNames:
                        if dirName in self.__dirFilter:
                            dirNames.remove(dirName)

                    for fileName in fileNames:
                        if fileName.endswith(".js") and fileName[0] != ".":
                            classObj = Class(os.path.join(dirPath, fileName), self)
                            className = classObj.getName()
                            
                            if className in classes:
                                raise Exception("Class duplication detected: %s and %s" % (classObj.getPath(), classes[className].getPath()))
                                
                            classes[className] = classObj
                
            logging.debug("Project %s contains %s classes", self.__name, len(classes))
            self.classes = classes
            return classes


    def getAssets(self):
        """ Returns all project asssets (images, stylesheets, etc.) """
        
        if self.__assetPath is None:
            return None
        
        try:
            return self.assets
            
        except AttributeError:
            assetPath = os.path.join(self.__path, self.__assetPath)
            assets = {}
            package = self.__package

            if assetPath and os.path.exists(assetPath):
                assetPathLen = len(assetPath) + 1
                for dirPath, dirNames, fileNames in os.walk(assetPath):
                    for dirName in dirNames:
                        if dirName in self.__dirFilter:
                            dirNames.remove(dirName)

                    for fileName in fileNames:
                        if fileName in ("manifest.json", "generate.py", "cache"):
                            continue
                            
                        if fileName[0] == "." or fileName.endswith((".js", ".txt", ".md")):
                            continue

                        filePath = os.path.join(dirPath, fileName)
                        relPath = filePath[assetPathLen:]
                        
                        # Support for pre-fixed package which is not used in filesystem, but in assets
                        if package:
                            name = "%s%s%s" % (package, os.sep, relPath)
                        else:
                            name = relPath
                            
                        # always using unix paths for the asset ID
                        assets[name.replace(os.sep, "/")] = filePath
                    
            logging.debug("Project %s contains %s assets", self.__name, len(assets))
            self.assets = assets
            return assets


    def getTranslations(self):
        """ Returns all translation files (gettext *.po files)"""
        
        if self.__translationPath is None:
            return None
        
        try:
            return self.translations
            
        except AttributeError:
            translationPath = os.path.join(self.__path, self.__translationPath)
            translations = {}

            if translationPath and os.path.exists(translationPath):
                for dirPath, dirNames, fileNames in os.walk(translationPath):
                    for dirName in dirNames:
                        if dirName in self.__dirFilter:
                            dirNames.remove(dirName)

                    for fileName in fileNames:    
                        if fileName[0] == "." or not fileName.endswith(".po"):
                            continue

                        translations[os.path.splitext(fileName)[0]] = os.path.join(dirPath, fileName)
            
            logging.debug("Project %s contains %s translations", self.__name, len(translations))
            self.translations = translations
            return translations
Exemple #10
0
class Project():
    
    kind = "none"
    
    def __init__(self, path, config=None, version=None):
        """
        Constructor call of the project. 

        - First param is the path of the project relative to the current working directory.
        - Config can be read from jasyproject.json or using constructor parameter @config
        - Parent is used for structural debug messages (dependency trees)
        """
        
        if not os.path.isdir(path):
            raise JasyError("Invalid project path: %s" % path)
        
        # Only store and work with full path
        self.__path = os.path.abspath(os.path.expanduser(path))
        
        # Store given params
        self.version = version
        
        # Intialize item registries
        self.classes = {}
        self.assets = {}        
        self.docs = {}
        self.translations = {}

        # Load project configuration
        self.__config = Config(config)
        self.__config.loadValues(os.path.join(self.__path, "jasyproject"), optional=True)

        # Initialize cache
        try:
            self.__cache = Cache(self.__path)
        except IOError as err:
            raise JasyError("Could not initialize project. Cache file in %s could not be initialized! %s" % (self.__path, err))
        
        # Read name from manifest or use the basename of the project's path
        self.__name = self.__config.get("name", getProjectNameFromPath(self.__path))
            
        # Read requires
        self.__requires = self.__config.get("requires", {})
        
        # Defined whenever no package is defined and classes/assets are not stored in the toplevel structure.
        self.__package = self.__config.get("package", self.__name if self.__config.has("name") else None)

        # Read fields (for injecting data into the project and build permutations)
        self.__fields = self.__config.get("fields", {})



    #
    # Project Scan/Init
    #

    def scan(self):
        
        # Processing custom content section. Only supports classes and assets.
        if self.__config.has("content"):
            self.kind = "manual"
            self.__addContent(self.__config.get("content"))

        # Application projects
        elif self.__hasDir("source"):
            self.kind = "application"

            if self.__hasDir("source/class"):
                self.__addDir("source/class", "classes")
            if self.__hasDir("source/asset"):
                self.__addDir("source/asset", "assets")
            if self.__hasDir("source/translation"):
                self.__addDir("source/translation", "translations")
                
        # Compat - please change to class/style/asset instead
        elif self.__hasDir("src"):
            self.kind = "resource"
            self.__addDir("src", "classes")

        # Resource projects
        else:
            self.kind = "resource"

            if self.__hasDir("class"):
                self.__addDir("class", "classes")
            if self.__hasDir("asset"):
                self.__addDir("asset", "assets")
            if self.__hasDir("translation"):
                self.__addDir("translation", "translations")

        # Generate summary
        summary = []
        for section in ["classes", "assets", "translations"]:
            content = getattr(self, section, None)
            if content:
                summary.append("%s %s" % (len(content), section))

        # Import library methods
        libraryPath = os.path.join(self.__path, "jasylibrary.py")
        if os.path.exists(libraryPath):
            methodNumber = loadLibrary(self.__name, libraryPath)
            summary.append("%s methods" % methodNumber)

        # Print out
        if summary:
            info("Scanned %s %s: %s" % (colorize(self.__name, "bold"), colorize("[%s]" % self.kind, "grey"), colorize(", ".join(summary), "green")))
        else:
            error("Project %s is empty!", self.__name)





    #
    # FILE SYSTEM INDEXER
    #
    
    def __hasDir(self, directory):
        full = os.path.join(self.__path, directory)
        if os.path.exists(full):
            if not os.path.isdir(full):
                raise JasyError("Expecting %s to be a directory: %s" % full)
            
            return True
        
        return False
        
        
    def __addContent(self, content):
        debug("Adding manual content")
        
        indent()
        for fileId in content:
            fileContent = content[fileId]
            if len(fileContent) == 0:
                raise JasyError("Empty content!")
                
            # If the user defines a file extension for JS public idenfiers 
            # (which is not required) we filter them out
            if fileId.endswith(".js"):
                raise JasyError("JavaScript files should define the exported name, not a file name: %s" % fileId)

            fileExtension = os.path.splitext(fileContent[0])[1]
            
            # 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]
            
            # Structure files
            if fileExtension in classExtensions:
                construct = Class
                dist = self.classes
            elif fileExtension in translationExtensions:
                construct = Translation
                dist = self.translations
            else:
                construct = Asset
                dist = self.assets
                
            # Check for duplication
            if fileId in dist:
                raise JasyError("Item ID was registered before: %s" % fileId)
            
            # Create instance
            item = construct(self, fileId).attach(filePath)
            debug("Registering %s %s" % (item.kind, fileId))
            dist[fileId] = item
            
        outdent()
        
        
    def __addDir(self, directory, distname):
        
        debug("Scanning directory: %s" % directory)
        indent()
        
        path = os.path.join(self.__path, directory)
        if not os.path.exists(path):
            return
            
        for dirPath, dirNames, fileNames in os.walk(path):
            for dirName in dirNames:
                # Filter dotted directories like .git, .bzr, .hg, .svn, etc.
                if dirName.startswith("."):
                    dirNames.remove(dirName)

                # Filter sub projects
                if os.path.exists(os.path.join(dirPath, dirName, "jasyproject.json")):
                    dirNames.remove(dirName)
                    
            relDirPath = os.path.relpath(dirPath, path)

            for fileName in fileNames:
                
                if fileName[0] == ".":
                    continue
                    
                relPath = os.path.normpath(os.path.join(relDirPath, fileName)).replace(os.sep, "/")
                fullPath = os.path.join(dirPath, fileName)
                
                self.addFile(relPath, fullPath, distname)
        
        outdent()


    def addFile(self, relPath, fullPath, distname, override=False):
        
        fileName = os.path.basename(relPath)
        fileExtension = os.path.splitext(fileName)[1]

        # Prepand package
        if self.__package:
            fileId = "%s/" % self.__package
        else:
            fileId = ""



        # Structure files  
        if fileExtension in classExtensions and distname == "classes":
            fileId += os.path.splitext(relPath)[0]
            construct = Class
            dist = self.classes
        elif fileExtension in translationExtensions and distname == "translations":
            fileId += os.path.splitext(relPath)[0]
            construct = Translation
            dist = self.translations
        elif fileName in docFiles:
            fileId += os.path.dirname(relPath)
            fileId = fileId.strip("/") # edge case when top level directory
            construct = Doc
            dist = self.docs
        else:
            fileId += relPath
            construct = Asset
            dist = self.assets

        # Only assets keep unix style paths identifiers
        if construct != Asset:
            fileId = fileId.replace("/", ".")

        # Check for duplication
        if fileId in dist and not override:
            raise JasyError("Item ID was registered before: %s" % fileId)

        # Create instance
        item = construct(self, fileId).attach(fullPath)
        debug("Registering %s %s" % (item.kind, fileId))
        dist[fileId] = item
        
        
    

    #
    # ESSENTIALS
    #
    
    def getRequires(self, prefix="external"):
        """
        Return the project requirements as project instances
        """

        global projects
        
        result = []
        
        for entry in self.__requires:
            
            if type(entry) is dict:
                source = entry["source"]
                config = getKey(entry, "config")
                version = getKey(entry, "version")
                kind = getKey(entry, "kind")
            else:
                source = entry
                config = None
                version = None
                kind = None

            revision = None
            
            if isRepository(source):
                kind = kind or getRepositoryType(source)
                path = os.path.abspath(os.path.join(prefix, getRepositoryFolder(source, version, kind)))
                
                # Only clone and update when the folder is unique in this session
                # This reduces git/hg/svn calls which are typically quite expensive
                if not path in projects:
                    revision = updateRepository(source, version, path)
                    if revision is None:
                        raise JasyError("Could not update repository %s" % source)
            
            else:
                kind = "local"
                if not source.startswith(("/", "~")):
                    path = os.path.join(self.__path, source)
                else:
                    path = os.path.abspath(os.path.expanduser(source))
            
            if path in projects:
                project = projects[path]
                
            else:
                fullversion = []
                
                # Produce user readable version when non is defined
                if version is None and revision is not None:
                    version = "master"
                
                if version is not None:
                    if "/" in version:
                        fullversion.append(version[version.rindex("/")+1:])
                    else:
                        fullversion.append(version)
                    
                if revision is not None:
                    # Shorten typical long revisions as used by e.g. Git
                    if type(revision) is str and len(revision) > 20:
                        fullversion.append(revision[:10])
                    else:
                        fullversion.append(revision)
                        
                if fullversion:
                    fullversion = "-".join(fullversion)
                else:
                    fullversion = None

                project = Project(path, config, fullversion)
                projects[path] = project
            
            result.append(project)
        
        return result


    def getFields(self):
        """ Return the project defined fields which may be configured by the build script """
        return self.__fields


    def getClassByName(self, className):
        """ Finds a class by its name."""

        try:
            return self.getClasses()[className]
        except KeyError:
            return None

    def getName(self):
        return self.__name
    
    def getPath(self):
        return self.__path
    
    def getPackage(self):
        return self.__package

    def getConfigValue(self, key, default=None):
        return self.__config.get(key, default)
        
    def toRelativeUrl(self, path, prefix="", subpath="source"):
        root = os.path.join(self.__path, subpath)
        relpath = os.path.relpath(path, root)

        if prefix:
            if not prefix[-1] == os.sep:
                prefix += os.sep
                
            relpath = os.path.normpath(prefix + relpath)
            
        return relpath.replace(os.sep, "/")



    #
    # CACHE API
    #
    
    def getCache(self):
        """Returns the cache instance"""
        
        return self.__cache
    
    def clean(self):
        """Clears the cache of the project"""
        
        info("Clearing cache of %s..." % self.__name)
        self.__cache.clear()
        
    def close(self):
        """Closes the project which deletes the internal caches"""
        
        self.__cache.close()
        self.__cache = None
        
        self.classes = None
        self.assets = None
        self.docs = None
        self.translations = None
        
    def pause(self):
        """Pauses the project so that other processes could modify/access it"""
        
        self.__cache.close()
        
    def resume(self):
        """Resumes the paused project"""
        
        self.__cache.open()



    #
    # LIST ACCESSORS
    #
    
    def getDocs(self):
        """Returns all package docs"""
        return self.docs

    def getClasses(self):
        """ Returns all project JavaScript classes. Requires all files to have a "js" extension. """
        return self.classes

    def getAssets(self):
        """ Returns all project asssets (images, stylesheets, static data, etc.). """
        return self.assets

    def getTranslations(self):
        """ Returns all translation files. Supports gettext style PO files with .po extension. """
        return self.translations