首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >pjen -简单的静态网站生成器

pjen -简单的静态网站生成器
EN

Code Review用户
提问于 2015-12-15 23:00:24
回答 1查看 80关注 0票数 6

我创建了一个简单的静态网站生成器,名为pjen。GitHub存储库可以找到这里

代码语言:javascript
复制
import os

class pjen:
    def __init__(self, path=None):
        """
        By default, the path will be set to the current working directory, 
        or the path can be specified using the 'path' keyword parameter.
        """

        if path is None:
            self.path = os.getcwd() 
        else:
            self.path = path

        self.path += "/website"

    def create_project(self):
        """
        Creates an initial file structure for a project at the specified path. 

        An exception is raised if a project already exists in the desired location.

        Creates the following file structure:

        website
          |-> templates
              |-> group1
          |-> images
          |-> scss
          |-> css
          |-> scripts

        """

        if not os.path.exists(self.path):

            #make the root directory
            os.makedirs(self.path)

            os.makedirs(self.path+"/templates")
            os.makedirs(self.path+"/templates/group1")          
            os.makedirs(self.path+"/images")
            os.makedirs(self.path+"/scss")
            os.makedirs(self.path+"/css")
            os.makedirs(self.path+"/scripts")

            print("Created project in {}".format(self.path))

        else:
            raise IOError("A directory already exists at: {}".format(self.path))


    def _count_indent(self, str, tab_equiv=4):
        """
        Returns the number of leading spaces. A tab is counted as 'tab_equiv' spaces.
        """

        i = 0
        count = 0

        while(str[i] == " " or str[i] == "\t"):

            if str[i] == " ":
                count += 1
            if str[i] == "\t":
                count += 4

            i += 1

        return count

    def _sanatise_file_list(self, l, fname=None):
        """
        Removes blacklisted files from the input list 'l'. In addition, a file with 
        the name 'fname' can also be removed.
        """

        blacklist = [".DS_Store"]

        if fname:
            blacklist.append(fname)

        for item in blacklist:
            try:
                l.pop(l.index(item))
            except ValueError:
                pass

    def generate(self):
        """
        Iterates through all the directories in the 'templates' directory, inserting all the 
        inputs into each template. 
        """

        #get a list of template groups
        static_groups = os.listdir(self.path+"/templates")
        self._sanatise_file_list(static_groups)

        print("Found {} group(s)".format(len(static_groups)))

        #iterate through each of the template groups
        for group in static_groups:

            #get each of the files in the group
            files = os.listdir(self.path+"/templates/"+group)

            #remove the template and hidden file
            self._sanatise_file_list(files, "template.html")

            #open the template 
            with open(self.path+"/templates/" + group + "/template.html", "r") as template:

                #iterate though the files that need to be generated
                for f in files:

                    #create a new static page
                    with open(self.path + "/" + f, "w") as page:

                        print("Generating file: {}".format(f))

                        #open up the input
                        with open(self.path + "/templates/" + group + "/" + f, "r") as my_input:

                            #iterate through the input and extract the various sections
                            css = ""
                            html = ""
                            scripts = ""

                            #flag to determine the section of the input file
                            in_section = None

                            #iterate through the input file and set the appropriate flag
                            for line in my_input.readlines():
                                if line.lstrip().startswith("{{ css }}"):
                                    in_section = "css"
                                elif line.lstrip().startswith("{{ html }}"):
                                    in_section = "html"
                                elif line.lstrip().startswith("{{ scripts }}"):
                                    in_section = "scripts"
                                else:
                                    if in_section == "css":
                                        css += line
                                    if in_section == "html":
                                        html += line
                                    if in_section == "scripts":
                                        scripts += line


                            #reset the file pointer as the template can be read many times
                            template.seek(0)

                            #iterate through the template file
                            for line in template.readlines():

                                if line.lstrip().startswith("{{ css }}"):

                                    indent = self._count_indent(line)

                                    #if there is css in the input file then insert it
                                    if css != "":

                                        for insert_line in [x + "\n" for x in css.split("\n")]:

                                            page.write(" "*indent + insert_line)

                                #if there is html in the input file then insert it
                                elif line.lstrip().startswith("{{ html }}"):

                                    indent = self._count_indent(line)

                                    if html != "":

                                        for insert_line in [x + "\n" for x in html.split("\n")]:

                                            page.write(" "*indent + insert_line)

                                #if there are script links in the input file then insert them
                                elif line.lstrip().startswith("{{ scripts }}"):

                                    indent = self._count_indent(line)

                                    if scripts != "":

                                        for insert_line in [x + "\n" for x in scripts.split("\n")]:

                                            page.write(" "*indent + insert_line)

                                #otherwise copy the template text
                                else:
                                    page.write(line)


if __name__ == "__main__":
    p = pjen()
    p.generate()
EN

回答 1

Code Review用户

回答已采纳

发布于 2015-12-16 15:05:43

create_project没有最好的名字。如果你想准确的话,它应该是create_project_folders。即使您不想长篇大论,您的print调用也应该更清楚它到底做了什么。我将翻转您的测试,并将if os.path.exists(self.path)用于raise IOError,这样函数的其余部分就不需要嵌套。请注意,它也更好地显示了流。伪码:

代码语言:javascript
复制
create_project function:
    if project folder exists:
        raise error

    make folders

说到创建文件夹,每次都会用新字符串重复调用相同的函数。这是使用for循环的最佳位置。此外,字符串实际上应该是常量。可能是类级别上的一组常量:

代码语言:javascript
复制
class pjen:

    PROJECT_FOLDERS = (
                        "/templates",
                        "/templates/group1",
                        "/images",
                        "/scss",
                        "/css",
                        "/scripts",
                      )

现在,您可以在这些文件夹中迭代,在create_project中创建它们:

代码语言:javascript
复制
def create_project(self):

    if os.path.exists(self.path):
        raise IOError("A directory already exists at: {}".format(self.path))

    for folder in pjen.PROJECT_FOLDERS:
        os.makedirs(self.path + folder)

    print("Created project in {}".format(self.path))

请注意,我没有包括os.makedirs(self.path)。这是因为Python使得所有目录都是必需的,而不仅仅是列表(事实上,这就是为什么makedirs而不是makedir)中的最后一个目录。同样,不需要调用os.makedirs(self.path + "/templates")和`os.makedirs(self.path +“/self.path/group1 1”),因为后者将创建两个文件夹。但是,我将其保持为常量,这样您就可以看到创建的文件夹的完整列表,因为这样可以提高可读性,并且您可能希望/需要在某个时候遍历所有项目文件夹。

不要使用str作为变量名,因为它会隐藏内置的str函数。使用string通常更好。我也不喜欢以下划线开头的_count_indent。这是一种惯例,用来表示函数是私有的,不应该在外部使用。我不明白为什么这里应该是这样的,除此之外,你不希望人们需要它。但我会亲自把下划线关掉。

对于输入列表来说,l是一个不好的名称,无论是input_list还是file_list都会更好。如果fnameNone,那么就可以了,因为它不会对字符串列表产生任何影响。此外,与其将单个fname附加到blacklist中,还可以创建一个元组:

代码语言:javascript
复制
    blacklist = (".DS_Store", fname)

事实上,我会直接迭代,而不是把它分配给黑名单。

此外,您还使用pop来删除项,但您可以只使用removepop在删除该值后返回值,如果不需要该值,则不需要pop

代码语言:javascript
复制
def _sanatise_file_list(self, l, fname=None):
    """
    Removes blacklisted files from the input list 'l'. In addition, a file with 
    the name 'fname' can also be removed.
    """

    for item in (".DS_Store", fname):
        try:
            l.pop(l.index(item))
        except ValueError:
            pass

generate作为一个函数太长了。你应该把它分解成更小的函数。理想情况下,每个函数只执行一项任务。这有助于可读性、清晰度和测试。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/114102

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档