我还想对我的静态博客生成器脚本进行另一次审查,以求改进。
#
#Static blogs generator.
#See https://github.com/st-kurilin/blogator for details.
#
#Main script. Used to build final script using build.py script.
#
###Files operations
#separated to make testing easier
"""Default values for some files.
Used to distribute script as a single file.
Actual values filled by build.py script."""
PREDEFINED = {}
def read(path):
"""Reads file content from FS"""
if path in PREDEFINED:
return PREDEFINED[path]
with open(path.as_posix()) as file:
return file.read()
def write(path, content):
"""Writes file content to FS"""
with open(path.as_posix(), 'w') as file:
file.write(content)
def copy(from_p, to_p):
"""Copies file content"""
import shutil
shutil.copyfile(from_p.as_posix(), to_p.as_posix())
def file_exist(path):
"""Check if file exist for specified path"""
return path.is_file()
###Markdown template engine operations
def md_read(inp):
"""Reads markdown formatted message."""
import markdown
md_converter = markdown.Markdown(extensions=['meta'])
content = md_converter.convert(inp)
meta = getattr(md_converter, 'Meta', [])
return {
'meta' : meta,
'content' : content
}
def md_meta_get(meta, key, alt=None, single_value=True):
"""Reads value from markdown read message meta."""
if key in meta:
if single_value and meta[key]:
return meta[key][0]
else:
return meta[key]
return alt
###Pystache template engine operations
def pystached(template, data):
"""Applies data to pystache template"""
import pystache
pys_template = pystache.parse(template)
pys_renderer = pystache.Renderer()
return pys_renderer.render(pys_template, data)
###Meta files readers operations
def parse_blog_meta(blog_meta_content):
"""Reads general blog info from file."""
from functools import partial
meta = md_read(blog_meta_content)['meta']
get = partial(md_meta_get, meta)
favicon_file = get('favicon-file')
favicon_url = get('favicon-url', 'favicon.cc/favicon/169/1/favicon.png')
return {
'meta' : meta,
'title' : get('title', 'Blog'),
'annotation' : get('annotation', 'Blogging for living'),
'favicon-file' : favicon_file,
'favicon' : 'favicon.ico' if favicon_file else favicon_url,
'posts' : get('posts', [], False),
'disqus' : get('disqus'),
'ganalitics' : get('ganalitics'),
}
def parse_post(post_blob, post_blob_orig_name):
"""Reads post info from file."""
import datetime
from functools import partial
def reformat_date(inpf, outf, date):
"""Reformats dates from one specified format to other one."""
if date is None:
return None
return datetime.datetime.strptime(date, inpf).strftime(outf)
row_post = md_read(post_blob)
post = {}
post['meta'] = meta = row_post['meta']
get = partial(md_meta_get, meta)
post['content'] = row_post['content']
post['title'] = get('title', post_blob_orig_name)
post['brief'] = get('brief')
post['short_title'] = get('short_title', post['title'])
post['link_base'] = get('link', post_blob_orig_name + ".html")
post['link'] = './' + post['link_base']
post['published'] = reformat_date('%Y-%m-%d', '%d %b %Y',
get('published'))
return post
###Flow operations
def clean_target(target):
"""Cleans target directory. Hidden files will not be deleted."""
import os
import glob
tpath = target.as_posix()
if not os.path.exists(tpath):
os.makedirs(tpath)
for file in glob.glob(tpath + '/*'):
os.remove(file)
def generate(blog_path, templates, target):
"""Generates blog content. Target directory expected to be empty."""
def prepare_favicon(blog, blog_home_dir, target):
"""Puts favicon file in right place with right name."""
if blog['favicon-file']:
orig_path = blog_home_dir / blog['favicon-file']
destination_path = target / 'favicon.ico'
copy(orig_path, destination_path)
def marked_active_post(orig_posts, active_index):
"""Returns copy of original posts
with specified post marked as active"""
active_post = orig_posts[active_index]
posts_view = orig_posts.copy()
active_post = orig_posts[active_index].copy()
active_post['active?'] = True
posts_view[active_index] = active_post
return posts_view
def write_templated(template_path, out_path, data):
"""Generate templated content to file."""
write(out_path, pystached(read(template_path), data))
def fname(file_name):
"""file name without extension"""
from pathlib import Path
as_path = Path(file_name)
name = as_path.name
suffix = as_path.suffix
return name.rsplit(suffix, 1)[0]
def read_post(declared_path, work_dir):
"""Find location of post and read it"""
from pathlib import Path
candidates = [work_dir / declared_path, Path(declared_path)]
for candidate in candidates:
if file_exist(candidate):
return read(candidate)
raise NameError("Tried find post file by [{}] but didn't find anything"
.format(', '.join([_.as_posix() for _ in candidates])))
blog = parse_blog_meta(read(blog_path))
work_dir = blog_path.parent
prepare_favicon(blog, work_dir, target)
posts = [parse_post(read_post(_, work_dir), fname(_))
for _ in blog['posts']]
for active_index, post in enumerate(posts):
posts_view = marked_active_post(posts, active_index)
write_templated(templates / "post.template.html",
target / post['link_base'],
{'blog': blog, 'posts': posts_view, 'post': post})
write_templated(templates / "index.template.html",
target / "index.html",
{'blog' : blog, 'posts': posts})
###Utils
def create_parser():
"""Parser factory method."""
import argparse
from pathlib import Path
parser = argparse.ArgumentParser(description='''Generates static blog
content from markdown posts.
''')
parser.add_argument('blog',
type=Path,
help='File with general information about blog',
default='blog')
parser.add_argument('-target',
type=Path,
help='generated content destination',
default='target')
parser.add_argument('-templates',
type=Path,
help='directory with templates',
default='blogtor-virtual/templates')
return parser
def main():
"""Start endpoint"""
args = create_parser().parse_args()
clean_target(args.target)
generate(args.blog, args.templates, args.target)我使用一个单一源文件来简化分发和导入功能,使其更加清晰。
发布于 2014-09-22 12:06:24
(代码看起来很棒,我仍然试图提供有用的建议。)
我使用单个源文件使分发变得更容易。
为什么一个.py文件更容易分发?我们有很多种方法来分发Python文件,比如git clone ... && python setup.py install,pip,轮子.
并在功能上进行导入,让自己更加清楚。
除了违反PEP 8,这意味着其他开发人员将难以理解(并坚持)您的约定。谈到PEP8,pip install flake8和flake8 myfile.py会有所帮助。我不会再评论词汇语法了。
###Files operations
#separated to make testing easier这些函数只会使代码复杂化,因为提供的抽象不足以补偿所增加的复杂性。
"""Default values for some files.
Used to distribute script as a single file.
Actual values filled by build.py script."""
PREDEFINED = {}
def read(path):
"""Reads file content from FS"""
if path in PREDEFINED:
return PREDEFINED[path]
with open(path.as_posix()) as file:
return file.read()这将不需要一个标准的方式来分发您的文件。此外,路径库文档建议str(path)而不是path.as_posix()。
def write(path, content):
"""Writes file content to FS"""
with open(path.as_posix(), 'w') as file:
file.write(content)既然您是在应用程序的上下文中,是否可以给write函数取一个更有意义的名称?
def copy(from_p, to_p):
"""Copies file content"""
import shutil
shutil.copyfile(from_p.as_posix(), to_p.as_posix())
def file_exist(path):
"""Check if file exist for specified path"""
return path.is_file()考虑一下path.is_file()的清晰性,但是这两个函数使代码变得复杂(我需要查找它们所做的事情),而shutil.copyfile和is_file()是标准的,更有可能为其他开发人员所了解。
###Markdown template engine operations
def md_read(inp):
"""Reads markdown formatted message."""
import markdown
md_converter = markdown.Markdown(extensions=['meta'])
content = md_converter.convert(inp)
meta = getattr(md_converter, 'Meta', [])md_converter.meta还不够吗?
return {
'meta' : meta,
'content' : content
}这很有趣。您可以将其视为要传递的规范数据结构,使用的是标准名称,而不是发送特定的meta和content。
def md_meta_get(meta, key, alt=None, single_value=True):
"""Reads value from markdown read message meta."""
if key in meta:
if single_value and meta[key]:
return meta[key][0]
else:
return meta[key]
return alt考虑使用meta_key = meta.get(key, alt)。
###Pystache template engine operations
def pystached(template, data):
"""Applies data to pystache template"""
import pystache
pys_template = pystache.parse(template)
pys_renderer = pystache.Renderer()
return pys_renderer.render(pys_template, data)很好的抽象!
###Meta files readers operations
def parse_blog_meta(blog_meta_content):
"""Reads general blog info from file."""
from functools import partial
meta = md_read(blog_meta_content)['meta']
get = partial(md_meta_get, meta)get有点泛泛而谈,令人困惑。保存几个按键有那么重要吗?
favicon_file = get('favicon-file')
favicon_url = get('favicon-url', 'favicon.cc/favicon/169/1/favicon.png')
return {
'meta' : meta,
'title' : get('title', 'Blog'),
'annotation' : get('annotation', 'Blogging for living'),
'favicon-file' : favicon_file,
'favicon' : 'favicon.ico' if favicon_file else favicon_url,
'posts' : get('posts', [], False),
'disqus' : get('disqus'),
'ganalitics' : get('ganalitics'),
}
def parse_post(post_blob, post_blob_orig_name):
"""Reads post info from file."""
import datetime
from functools import partial
def reformat_date(inpf, outf, date):
"""Reformats dates from one specified format to other one."""
if date is None:
return None
return datetime.datetime.strptime(date, inpf).strftime(outf)
row_post = md_read(post_blob)
post = {}
post['meta'] = meta = row_post['meta']
get = partial(md_meta_get, meta)
post['content'] = row_post['content']
post['title'] = get('title', post_blob_orig_name)
post['brief'] = get('brief')
post['short_title'] = get('short_title', post['title'])
post['link_base'] = get('link', post_blob_orig_name + ".html")
post['link'] = './' + post['link_base']这有什么意义呢?如果没有post['link_base']的话,这不是等价的吗?
post['published'] = reformat_date('%Y-%m-%d', '%d %b %Y',
get('published'))
return post
###Flow operations
def clean_target(target):
"""Cleans target directory. Hidden files will not be deleted."""
import os
import glob
tpath = target.as_posix()
if not os.path.exists(tpath):
os.makedirs(tpath)
for file in glob.glob(tpath + '/*'):
os.remove(file)很好的抽象。确保您的用户可以理解任何故障。
def generate(blog_path, templates, target):
"""Generates blog content. Target directory expected to be empty."""我喜欢嵌套函数,但请承认“请把我放在我自己的模块中!”:)
def prepare_favicon(blog, blog_home_dir, target):
"""Puts favicon file in right place with right name."""
if blog['favicon-file']:
orig_path = blog_home_dir / blog['favicon-file']
destination_path = target / 'favicon.ico'
copy(orig_path, destination_path)
def marked_active_post(orig_posts, active_index):
"""Returns copy of original posts
with specified post marked as active"""
active_post = orig_posts[active_index]
posts_view = orig_posts.copy()
active_post = orig_posts[active_index].copy()
active_post['active?'] = True
posts_view[active_index] = active_post
return posts_view所以..。orig_posts[active_index]['active?'] = True不等同于marked_active_post[orig_posts, active_index]吗?
def write_templated(template_path, out_path, data):
"""Generate templated content to file."""
write(out_path, pystached(read(template_path), data))
def fname(file_name):
"""file name without extension"""
from pathlib import Path
as_path = Path(file_name)
name = as_path.name
suffix = as_path.suffix
return name.rsplit(suffix, 1)[0]使用return Path(file_name).stem。
def read_post(declared_path, work_dir):
"""Find location of post and read it"""
from pathlib import Path
candidates = [work_dir / declared_path, Path(declared_path)]
for candidate in candidates:
if file_exist(candidate):
return read(candidate)
raise NameError("Tried find post file by [{}] but didn't find anything"
.format(', '.join([_.as_posix() for _ in candidates])))
blog = parse_blog_meta(read(blog_path))
work_dir = blog_path.parent
prepare_favicon(blog, work_dir, target)
posts = [parse_post(read_post(_, work_dir), fname(_))
for _ in blog['posts']]
for active_index, post in enumerate(posts):
posts_view = marked_active_post(posts, active_index)
write_templated(templates / "post.template.html",
target / post['link_base'],
{'blog': blog, 'posts': posts_view, 'post': post})
write_templated(templates / "index.template.html",
target / "index.html",
{'blog' : blog, 'posts': posts})
###Utils
def create_parser():
"""Parser factory method."""
import argparse
...谢谢您使用argparse (顺便说一下,还有pathlib.Path )。还有比create_parser和Parser factory method更好的吗?这些注释适用于任何argparse使用。
def main():
"""Start endpoint"""
args = create_parser().parse_args()
clean_target(args.target)
generate(args.blog, args.templates, args.target)https://codereview.stackexchange.com/questions/63174
复制相似问题