def init(project, theme): """Initialize a site project.""" if not opx(project): os.makedirs(project) if os.listdir(project): print("abort : project dir %s is not empty" % project) sys.exit(1) dir_in = opj(project, "input") dir_out = opj(project, "output") os.mkdir(dir_in) os.mkdir(dir_out) for fname, content in EXAMPLE_FILES.items(): print('info: create example %r' % fname) with open(opj(project, fname), 'w') as fp: fp.write(content) if theme != 'minimal': shutil.copy(opj(THEME_DIR, theme, 'page.html'), project) for fname in glob.glob(opj(THEME_DIR, theme, '*')): print('info: copy theme data %r' % fname) if os.path.basename(fname) == 'page.html': continue if os.path.isdir(fname): shutil.copytree(fname, opj(dir_in, os.path.basename(fname))) else: shutil.copy(fname, dir_in) print("success: initialized project")
def prepare_output(macmod): if hasattr(macmod, "prepare_output"): return macmod.prepare_output() print "Using built-in prepare." project = macmod.project dir_in = macmod.input dir_out = macmod.output # prepare output directory project_path = os.path.realpath(project) for fod in glob.glob(opj(dir_out, "*")): # don't remove the sources """ real_fod = os.path.realpath(fod) if real_fod == project_path or real_fod.startswith(project_path): print real_fod, project_path continue """ if os.path.isdir(fod): print "-dir", fod shutil.rmtree(fod) else: os.remove(fod) if not opx(dir_out): os.mkdir(dir_out)
def init(project='carlae_page', theme=None, output='docs', default_readme=False): """Initialize a site project.""" HERE = os.path.dirname(os.path.realpath(__file__)) THEME_DIR = opj(HERE, 'themes') cwd = os.getcwd() check_mkdir(opj(cwd, project)) dir_out = opj(cwd, output) check_mkdir(dir_out) theme_dir = opj(project, "theme") if not (opx(theme_dir)): if theme is None: theme = 'skeleton' elif not (theme in os.listdir(THEME_DIR)): print("the selected theme not in theme dir, will use skeleton") theme = 'skeleton' shutil.copytree(opj(THEME_DIR, theme), theme_dir) THEME_DIR = theme_dir static_dir = os.listdir(theme_dir) static_dir = [d for d in static_dir if not (d[0] == '_')] for d in static_dir: if not (opx(opj(dir_out, d))): shutil.copytree(opj(theme_dir, d), opj(dir_out, d)) if default_readme: for fname, content in EXAMPLE_FILES.items(): if not (opx(opj(cwd, fname))): print('info: create example %r' % fname) with open(opj(cwd, fname), 'w') as fp: fp.write(content) else: print( 'README.md file exists, skip making default readme.md file' ) print("success: initialized project")
def init(project, opts): """Initialize a site project.""" if not opx(project): os.makedirs(project) if os.listdir(project): print("error : project dir %s is not empty, abort" % project) sys.exit(1) os.mkdir(opj(project, "input")) os.mkdir(opj(project, opts.output_dir)) for fname, content in EXAMPLE_FILES.items(): with open(opj(project, fname), 'w') as fp: fp.write(content) print("success: initialized project")
def init(project): """Initialize a site project.""" if not opx(project): os.makedirs(project) if os.listdir(project): print("error : project dir %s is not empty, abort" % project) sys.exit(1) os.mkdir(opj(project, "input")) os.mkdir(opj(project, "output")) for fname, content in EXAMPLE_FILES.items(): with open(opj(project, fname), 'w') as fp: fp.write(content) print("success: initialized project")
def build(project, opts): """Build a site project.""" # ------------------------------------------------------------------------- # utilities # ------------------------------------------------------------------------- def abort_iex(page, itype, inline, exc): """Abort because of an exception in inlined Python code.""" print("abort : Python %s in %s failed" % (itype, page)) print((" %s raising the exception " % itype).center(79, "-")) print(inline) print(" exception ".center(79, "-")) print(exc) sys.exit(1) # ------------------------------------------------------------------------- # regex patterns and replacements # ------------------------------------------------------------------------- regx_escp = re.compile(r'\\((?:(?:<|<)!--|{)(?:{|%))') # escaped code repl_escp = r'\1' regx_rurl = re.compile(r'(?<=(?:(?:\n| )src|href)=")([^#/&%].*?)(?=")') repl_rurl = lambda m: urlparse.urljoin(opts.base_url, m.group(1)) regx_eval = re.compile(r'(?<!\\)(?:(?:<!--|{){)(.*?)(?:}(?:-->|}))', re.S) def repl_eval(m): """Replace a Python expression block by its evaluation.""" expr = m.group(1) try: repl = eval(expr, macros.copy()) except: abort_iex(page, "expression", expr, traceback.format_exc()) else: if not isinstance(repl, basestring): # e.g. numbers repl = unicode(repl) elif not isinstance(repl, unicode): repl = repl.decode("utf-8") return repl regx_exec = re.compile(r'(?<!\\)(?:(?:<!--|{)%)(.*?)(?:%(?:-->|}))', re.S) def repl_exec(m): """Replace a block of Python statements by their standard output.""" stmt = m.group(1).replace("\r\n", "\n") # base indentation ind_lvl = len(re.findall(r'^(?: *\n)*( *)', stmt, re.MULTILINE)[0]) ind_rex = re.compile(r'^ {0,%d}' % ind_lvl, re.MULTILINE) stmt = ind_rex.sub('', stmt) # execute sys.stdout = StringIO.StringIO() try: exec stmt in macros.copy() except: sys.stdout = sys.__stdout__ abort_iex(page, "statements", stmt, traceback.format_exc()) else: repl = sys.stdout.getvalue()[:-1] # remove last line break sys.stdout = sys.__stdout__ if not isinstance(repl, unicode): repl = repl.decode(opts.input_enc) return repl # ------------------------------------------------------------------------- # preparations # ------------------------------------------------------------------------- dir_in = opj(project, "input") dir_out = opj(project, "output") page_html = opj(project, "page.html") # check required files and folders for pelem in (page_html, dir_in, dir_out): if not opx(pelem): print( "abort : %s does not exist, looks like project has not been " "initialized" % pelem) sys.exit(1) # prepare output directory if not opts.dry_run: for fod in glob.glob(opj(dir_out, "*")): if os.path.isdir(fod): shutil.rmtree(fod) else: os.remove(fod) if not opx(dir_out): os.mkdir(dir_out) # macro module fname = opj(opts.project, "macros.py") macros = imp.load_source("macros", fname).__dict__ if opx(fname) else {} macros["__encoding__"] = opts.output_enc macros["options"] = opts macros["project"] = project macros["input"] = dir_in macros["output"] = dir_out # "builtin" items for use in macros and templates macros["hx"] = hx macros["htmlspecialchars"] = hx # legacy name of `htmlx` function macros["Page"] = Page # ------------------------------------------------------------------------- # process input files # ------------------------------------------------------------------------- Page._template = macros.get("page", {}) Page._opts = opts Page._pstrip = dir_in pages = [] custom_converter = macros.get('converter', {}) for cwd, dirs, files in os.walk(dir_in.decode(opts.filename_enc)): cwd_site = cwd[len(dir_in):].lstrip(os.path.sep) if not opts.dry_run: for sdir in dirs[:]: if re.search(opts.ignore, opj(cwd_site, sdir)): dirs.remove(sdir) else: os.mkdir(opj(dir_out, cwd_site, sdir)) for f in files: if re.search(opts.ignore, opj(cwd_site, f)): pass elif re.search(MKD_PATT, f): page = Page(opj(cwd, f)) pages.append(page) else: # either use a custom converter or do a plain copy for patt, (func, ext) in custom_converter.items(): if re.search(patt, f): f_src = opj(cwd, f) f_dst = opj(dir_out, cwd_site, f) f_dst = '%s.%s' % (os.path.splitext(f_dst)[0], ext) print('info : convert %s (%s)' % (f_src, func.__name__)) if not opts.dry_run: func(f_src, f_dst) break else: if not opts.dry_run: src = opj(cwd, f) try: shutil.copy(src, opj(dir_out, cwd_site)) except OSError: # some filesystems like FAT won't allow shutil.copy shutil.copyfile(src, opj(dir_out, cwd_site, f)) pages.sort(key=lambda p: int(p.get("sval", "0"))) macros["pages"] = pages # ------------------------------------------------------------------------- # run pre-convert hooks in macro module (named 'once' before) # ------------------------------------------------------------------------- hooks = [a for a in macros if re.match(r'hook_preconvert_|once_', a)] for fn in sorted(hooks): macros[fn]() # ------------------------------------------------------------------------- # convert pages (markdown to HTML) # ------------------------------------------------------------------------- for page in pages: print("info : convert %s" % page) # replace expressions and statements in page source macros["page"] = page out = regx_eval.sub(repl_eval, page.source) out = regx_exec.sub(repl_exec, out) # convert to HTML page.html = markdown.Markdown(extensions=opts.md_ext).convert(out) # ------------------------------------------------------------------------- # run post-convert hooks in macro module # ------------------------------------------------------------------------- hooks = [a for a in macros if a.startswith("hook_postconvert_")] for fn in sorted(hooks): macros[fn]() # ------------------------------------------------------------------------- # render complete HTML pages # ------------------------------------------------------------------------- with codecs.open(opj(project, "page.html"), 'r', opts.input_enc) as fp: default_template = fp.read() for page in pages: if 'template' in page: fname = opj(project, page['template']) with codecs.open(fname, 'r', opts.input_enc) as fp: template = fp.read() else: template = default_template print("info : render %s" % page.url) # replace expressions and statements in page.html macros["page"] = page macros["__content__"] = page.html out = regx_eval.sub(repl_eval, template) out = regx_exec.sub(repl_exec, out) # un-escape escaped python code blocks out = regx_escp.sub(repl_escp, out) # make relative links absolute out = regx_rurl.sub(repl_rurl, out) # write HTML page fname = page.fname.replace(dir_in, dir_out) fname = re.sub(MKD_PATT, ".html", fname) if not opts.dry_run: with codecs.open(fname, 'w', opts.output_enc) as fp: fp.write(out) if opts.dry_run: print("success: built project (dry-run)") else: print("success: built project")
def build(project, opts): """Build a site project.""" # ------------------------------------------------------------------------- # utilities # ------------------------------------------------------------------------- def abort_iex(page, itype, inline, exc): """Abort because of an exception in inlined Python code.""" print("error : Python %s in %s failed" % (itype, page.fname)) print((" %s raising the exception " % itype).center(79, "-")) print(inline) print(" exception ".center(79, "-")) print(exc) sys.exit(1) # ------------------------------------------------------------------------- # regex patterns and replacements # ------------------------------------------------------------------------- regx_escp = re.compile(r'\\((?:(?:<|<)!--|{)(?:{|%))') # escaped code repl_escp = r'\1' regx_rurl = re.compile(r'(?<=(?:(?:\n| )src|href)=")([^#/&%].*?)(?=")') repl_rurl = lambda m: urlparse.urljoin(opts.base_url, m.group(1)) regx_eval = re.compile(r'(?<!\\)(?:(?:<!--|{){)(.*?)(?:}(?:-->|}))', re.S) def repl_eval(m): """Replace a Python expression block by its evaluation.""" expr = m.group(1) try: repl = eval(expr, macros.copy()) except: abort_iex(page, "expression", expr, traceback.format_exc()) else: if not isinstance(repl, basestring): # e.g. numbers repl = unicode(repl) elif not isinstance(repl, unicode): repl = repl.decode("utf-8") return repl regx_exec = re.compile(r'(?<!\\)(?:(?:<!--|{)%)(.*?)(?:%(?:-->|}))', re.S) def repl_exec(m): """Replace a block of Python statements by their standard output.""" stmt = m.group(1).replace("\r\n", "\n") # base indentation ind_lvl = len(re.findall(r'^(?: *\n)*( *)', stmt, re.MULTILINE)[0]) ind_rex = re.compile(r'^ {0,%d}' % ind_lvl, re.MULTILINE) stmt = ind_rex.sub('', stmt) # execute sys.stdout = StringIO.StringIO() try: exec stmt in macros.copy() except: sys.stdout = sys.__stdout__ abort_iex(page, "statements", stmt, traceback.format_exc()) else: repl = sys.stdout.getvalue()[:-1] # remove last line break sys.stdout = sys.__stdout__ if not isinstance(repl, unicode): repl = repl.decode(opts.input_enc) return repl # ------------------------------------------------------------------------- # preparations # ------------------------------------------------------------------------- dir_in = opj(project, "input") dir_out = opj(project, opts.output_dir) page_html = opj(project, "page.html") # check required files and folders for pelem in (page_html, dir_in, dir_out): if not opx(pelem): print("error : %s does not exist, looks like project has not been " "initialized, abort" % pelem) sys.exit(1) # macro module class nomod: pass # dummy module, something we can set attributes on fname = opj(opts.project, "macros.py") macros = {"__encoding__": opts.output_enc} macmod = opx(fname) and imp.load_source("macros", fname) or nomod setattr(macmod, "options", opts) setattr(macmod, "project", project) setattr(macmod, "input", dir_in) setattr(macmod, "output", dir_out) for attr in dir(macmod): if not attr.startswith("_"): macros[attr] = getattr(macmod, attr) prepare_output(macmod) pages = [] for _fn in dir(macmod): if _fn.startswith("init_"): getattr(macmod, _fn)(pages) # ------------------------------------------------------------------------- # process input files # ------------------------------------------------------------------------- page_global = macros.get("page", {}) for cwd, dirs, files in os.walk(dir_in.decode(opts.filename_enc)): cwd_site = cwd[len(dir_in):].lstrip(os.path.sep) for sdir in dirs[:]: if re.search(opts.ignore, opj(cwd_site, sdir)): dirs.remove(sdir) else: tmp = opj(dir_out, cwd_site, sdir) if not os.path.exists(tmp): os.mkdir(tmp) for f in files: if re.search(opts.ignore, opj(cwd_site, f)): pass elif re.search(MKD_PATT, f): page = Page(page_global, opj(cwd, f), dir_in, opts) pages.append(page) else: shutil.copy(opj(cwd, f), opj(dir_out, cwd_site)) pages.sort(key=lambda p: int(p.get("sval", "0"))) macros["pages"] = pages macmod.pages = pages # ------------------------------------------------------------------------- # run pre-convert hooks in macro module (named 'once' before) # ------------------------------------------------------------------------- hooks = [a for a in dir(macmod) if re.match(r'hook_preconvert_|once_', a)] for fn in sorted(hooks): getattr(macmod, fn)() # ------------------------------------------------------------------------- # convert pages (markdown to HTML) # ------------------------------------------------------------------------- for page in sorted(pages, key=lambda p: p["url"]): print("info : convert %s" % page.fname) # replace expressions and statements in page source macmod.page = page macros["page"] = page out = regx_eval.sub(repl_eval, page.source) out = regx_exec.sub(repl_exec, out) # convert to HTML page.html = markdown.Markdown(extensions=opts.md_ext).convert(out) # ------------------------------------------------------------------------- # run post-convert hooks in macro module # ------------------------------------------------------------------------- hooks = [a for a in dir(macmod) if a.startswith("hook_postconvert_")] for fn in sorted(hooks): getattr(macmod, fn)() # ------------------------------------------------------------------------- # render complete HTML pages # ------------------------------------------------------------------------- with codecs.open(opj(project, "page.html"), 'r', opts.input_enc) as fp: skeleton = fp.read() hooks = [a for a in dir(macmod) if a.startswith("hook_html_")] for page in sorted(pages, key=lambda p: p["url"]): print("info : render %s" % page.url) # replace expressions and statements in page.html macmod.page = page macros["page"] = page macros["__content__"] = page.html out = regx_eval.sub(repl_eval, skeleton) out = regx_exec.sub(repl_exec, out) # un-escape escaped python code blocks out = regx_escp.sub(repl_escp, out) # make relative links absolute out = regx_rurl.sub(repl_rurl, out) # apply final hooks for fn in hooks: out = getattr(macmod, fn)(out, page) # write HTML page fname = page.fname.replace(dir_in, dir_out) fname = re.sub(MKD_PATT, ".html", fname) with codecs.open(fname, 'w', opts.output_enc) as fp: fp.write(out) print("success: built project")
def check_mkdir(outpath): #directory = os.path.dirname(outpath) #print(directory) if not opx(outpath): print('creating dir:', outpath) os.makedirs(outpath)
def build(project, opts): """Build a site project.""" # ------------------------------------------------------------------------- # utilities # ------------------------------------------------------------------------- def abort_iex(page, itype, inline, exc): """Abort because of an exception in inlined Python code.""" print("abort : Python %s in %s failed" % (itype, page)) print((" %s raising the exception " % itype).center(79, "-")) print(inline) print(" exception ".center(79, "-")) print(exc) sys.exit(1) # ------------------------------------------------------------------------- # regex patterns and replacements # ------------------------------------------------------------------------- regx_escp = re.compile(r'\\((?:(?:<|<)!--|{)(?:{|%))') # escaped code repl_escp = r'\1' regx_rurl = re.compile(r'(?<=(?:(?:\n| )src|href)=")([^#/&%].*?)(?=")') repl_rurl = lambda m: urlparse.urljoin(opts.base_url, m.group(1)) regx_eval = re.compile(r'(?<!\\)(?:(?:<!--|{){)(.*?)(?:}(?:-->|}))', re.S) def repl_eval(m): """Replace a Python expression block by its evaluation.""" expr = m.group(1) try: repl = eval(expr, macros.copy()) except: abort_iex(page, "expression", expr, traceback.format_exc()) else: if not isinstance(repl, basestring): # e.g. numbers repl = unicode(repl) elif not isinstance(repl, unicode): repl = repl.decode("utf-8") return repl regx_exec = re.compile(r'(?<!\\)(?:(?:<!--|{)%)(.*?)(?:%(?:-->|}))', re.S) def repl_exec(m): """Replace a block of Python statements by their standard output.""" stmt = m.group(1).replace("\r\n", "\n") # base indentation ind_lvl = len(re.findall(r'^(?: *\n)*( *)', stmt, re.MULTILINE)[0]) ind_rex = re.compile(r'^ {0,%d}' % ind_lvl, re.MULTILINE) stmt = ind_rex.sub('', stmt) # execute sys.stdout = StringIO.StringIO() try: exec stmt in macros.copy() except: sys.stdout = sys.__stdout__ abort_iex(page, "statements", stmt, traceback.format_exc()) else: repl = sys.stdout.getvalue()[:-1] # remove last line break sys.stdout = sys.__stdout__ if not isinstance(repl, unicode): repl = repl.decode(opts.input_enc) return repl # ------------------------------------------------------------------------- # preparations # ------------------------------------------------------------------------- dir_in = opj(project, "input") dir_out = opj(project, "output") page_html = opj(project, "page.html") # check required files and folders for pelem in (page_html, dir_in, dir_out): if not opx(pelem): print("abort : %s does not exist, looks like project has not been " "initialized" % pelem) sys.exit(1) # prepare output directory for fod in glob.glob(opj(dir_out, "*")): if os.path.isdir(fod): shutil.rmtree(fod) else: os.remove(fod) if not opx(dir_out): os.mkdir(dir_out) # macro module fname = opj(opts.project, "macros.py") macros = imp.load_source("macros", fname).__dict__ if opx(fname) else {} macros["__encoding__"] = opts.output_enc macros["options"] = opts macros["project"] = project macros["input"] = dir_in macros["output"] = dir_out # "builtin" items for use in macros and templates macros["hx"] = hx macros["htmlspecialchars"] = hx # legacy name of `htmlx` function macros["Page"] = Page # ------------------------------------------------------------------------- # process input files # ------------------------------------------------------------------------- Page._template = macros.get("page", {}) Page._opts = opts Page._pstrip = dir_in pages = [] custom_converter = macros.get('converter', {}) for cwd, dirs, files in os.walk(dir_in.decode(opts.filename_enc)): cwd_site = cwd[len(dir_in):].lstrip(os.path.sep) for sdir in dirs[:]: if re.search(opts.ignore, opj(cwd_site, sdir)): dirs.remove(sdir) else: os.mkdir(opj(dir_out, cwd_site, sdir)) for f in files: if re.search(opts.ignore, opj(cwd_site, f)): pass elif re.search(MKD_PATT, f): page = Page(opj(cwd, f)) pages.append(page) else: # either use a custom converter or do a plain copy for patt, (func, ext) in custom_converter.items(): if re.search(patt, f): f_src = opj(cwd, f) f_dst = opj(dir_out, cwd_site, f) f_dst = '%s.%s' % (os.path.splitext(f_dst)[0], ext) print('info : convert %s (%s)' % (f_src, func.__name__)) func(f_src, f_dst) break else: src = opj(cwd, f) try: shutil.copy(src, opj(dir_out, cwd_site)) except OSError: # some filesystems like FAT won't allow shutil.copy shutil.copyfile(src, opj(dir_out, cwd_site, f)) pages.sort(key=lambda p: int(p.get("sval", "0"))) macros["pages"] = pages # ------------------------------------------------------------------------- # run pre-convert hooks in macro module (named 'once' before) # ------------------------------------------------------------------------- hooks = [a for a in macros if re.match(r'hook_preconvert_|once_', a)] for fn in sorted(hooks): macros[fn]() # ------------------------------------------------------------------------- # convert pages (markdown to HTML) # ------------------------------------------------------------------------- for page in pages: print("info : convert %s" % page) # replace expressions and statements in page source macros["page"] = page out = regx_eval.sub(repl_eval, page.source) out = regx_exec.sub(repl_exec, out) # convert to HTML extensions = opts.md_ext extensions.append('markdown.extensions.fenced_code') extensions.append('markdown.extensions.tables') extensions.append('markdown.extensions.admonition') extensions = list(set(extensions)) page.html = markdown.Markdown(extensions=extensions).convert(out) # ------------------------------------------------------------------------- # run post-convert hooks in macro module # ------------------------------------------------------------------------- hooks = [a for a in macros if a.startswith("hook_postconvert_")] for fn in sorted(hooks): macros[fn]() # ------------------------------------------------------------------------- # render complete HTML pages # ------------------------------------------------------------------------- with codecs.open(opj(project, "page.html"), 'r', opts.input_enc) as fp: skeleton = fp.read() for page in pages: print("info : render %s" % page.url) # replace expressions and statements in page.html macros["page"] = page macros["__content__"] = page.html out = regx_eval.sub(repl_eval, skeleton) out = regx_exec.sub(repl_exec, out) # un-escape escaped python code blocks out = regx_escp.sub(repl_escp, out) # make relative links absolute #out = regx_rurl.sub(repl_rurl, out) # write HTML page fname = page.fname.replace(dir_in, dir_out) fname = re.sub(MKD_PATT, ".html", fname) with codecs.open(fname, 'w', opts.output_enc) as fp: fp.write(out) print("success: built project")
def build(project, opts): """Build a site project.""" # ------------------------------------------------------------------------- # utilities # ------------------------------------------------------------------------- def abort_iex(page, itype, inline, exc): """Abort because of an exception in inlined Python code.""" print("error : Python %s in %s failed" % (itype, page.fname)) print((" %s raising the exception " % itype).center(79, "-")) print(inline) print(" exception ".center(79, "-")) print(exc) sys.exit(1) # ------------------------------------------------------------------------- # regex patterns and replacements # ------------------------------------------------------------------------- regx_escp = re.compile(r'\\((?:(?:<|<)!--|{)(?:{|%))') # escaped code repl_escp = r'\1' regx_rurl = re.compile(r'(?<=(?:(?:\n| )src|href)=")([^#/&%].*?)(?=")') repl_rurl = lambda m: urlparse.urljoin(opts.base_url, m.group(1)) regx_eval = re.compile(r'(?<!\\)(?:(?:<!--|{){)(.*?)(?:}(?:-->|}))', re.S) def repl_eval(m): """Replace a Python expression block by its evaluation.""" expr = m.group(1) try: repl = eval(expr, macros.copy()) except: abort_iex(page, "expression", expr, traceback.format_exc()) else: if not isinstance(repl, basestring): # e.g. numbers repl = unicode(repl) elif not isinstance(repl, unicode): repl = repl.decode("utf-8") return repl regx_exec = re.compile(r'(?<!\\)(?:(?:<!--|{)%)(.*?)(?:%(?:-->|}))', re.S) def repl_exec(m): """Replace a block of Python statements by their standard output.""" stmt = m.group(1).replace("\r\n", "\n") # base indentation ind_lvl = len(re.findall(r'^(?: *\n)*( *)', stmt, re.MULTILINE)[0]) ind_rex = re.compile(r'^ {0,%d}' % ind_lvl, re.MULTILINE) stmt = ind_rex.sub('', stmt) # execute sys.stdout = StringIO.StringIO() try: exec stmt in macros.copy() except: sys.stdout = sys.__stdout__ abort_iex(page, "statements", stmt, traceback.format_exc()) else: repl = sys.stdout.getvalue()[:-1] # remove last line break sys.stdout = sys.__stdout__ if not isinstance(repl, unicode): repl = repl.decode(opts.input_enc) return repl # ------------------------------------------------------------------------- # preparations # ------------------------------------------------------------------------- dir_in = opj(project, "input") dir_out = opj(project, OUTPUT_DIR) page_html = opj(project, "page.html") # check required files and folders for pelem in (page_html, dir_in, dir_out): if not opx(pelem): print( "error : %s does not exist, looks like project has not been " "initialized, abort" % pelem) sys.exit(1) # prepare output directory project_path = os.path.realpath(project) for fod in glob.glob(opj(dir_out, "*")): # don't remove the sources real_fod = os.path.realpath(fod) if real_fod == project_path or real_fod.startswith(project_path + os.path.sep): continue # don't remove version control if os.path.basename(fod) in ('.git', '.hg', '.svn'): continue if os.path.isdir(fod): shutil.rmtree(fod) else: os.remove(fod) if not opx(dir_out): os.mkdir(dir_out) # macro module class nomod: pass # dummy module, something we can set attributes on fname = opj(opts.project, "macros.py") macros = {"__encoding__": opts.output_enc} macmod = opx(fname) and imp.load_source("macros", fname) or nomod setattr(macmod, "options", opts) setattr(macmod, "project", project) setattr(macmod, "input", dir_in) setattr(macmod, "output", dir_out) for attr in dir(macmod): if not attr.startswith("_"): macros[attr] = getattr(macmod, attr) # ------------------------------------------------------------------------- # process input files # ------------------------------------------------------------------------- pages = [] page_global = macros.get("page", {}) for cwd, dirs, files in os.walk(dir_in.decode(opts.filename_enc)): cwd_site = cwd[len(dir_in):].lstrip(os.path.sep) for sdir in dirs[:]: if re.search(opts.ignore, opj(cwd_site, sdir)): dirs.remove(sdir) else: os.mkdir(opj(dir_out, cwd_site, sdir)) for f in files: if re.search(opts.ignore, opj(cwd_site, f)): pass elif re.search(MKD_PATT, f): page = Page(page_global, opj(cwd, f), dir_in, opts) pages.append(page) else: shutil.copy(opj(cwd, f), opj(dir_out, cwd_site)) macros["pages"] = pages pages.sort(key=lambda p: p.get('fname') ) # FIXME: some pages are lost without this, WTF?! macmod.pages = pages # ------------------------------------------------------------------------- # run pre-convert hooks in macro module (named 'once' before) # ------------------------------------------------------------------------- hooks = [a for a in dir(macmod) if re.match(r'hook_preconvert_|once_', a)] for fn in sorted(hooks): getattr(macmod, fn)() # ------------------------------------------------------------------------- # convert pages (markdown to HTML) # ------------------------------------------------------------------------- got = [] for page in pages: print("info : convert %s" % page.fname) if page.fname in got: print >> sys.stderr, "ERROR : %s appears twice" % page.fname continue got.append(page.fname) # replace expressions and statements in page source macmod.page = page macros["page"] = page out = regx_eval.sub(repl_eval, page.source) out = regx_exec.sub(repl_exec, out) # convert to HTML page.html = markdown.Markdown(extensions=opts.md_ext).convert(out) # ------------------------------------------------------------------------- # run post-convert hooks in macro module # ------------------------------------------------------------------------- hooks = [a for a in dir(macmod) if a.startswith("hook_postconvert_")] for fn in sorted(hooks): getattr(macmod, fn)() # ------------------------------------------------------------------------- # render complete HTML pages # ------------------------------------------------------------------------- with codecs.open(opj(project, "page.html"), 'r', opts.input_enc) as fp: skeleton = fp.read() pages.sort(key=lambda p: int(p.get("sval", "0"))) for page in pages: print("info : render %s" % page.url) # replace expressions and statements in page.html macmod.page = page macros["page"] = page macros["__content__"] = page.html out = regx_eval.sub(repl_eval, skeleton) out = regx_exec.sub(repl_exec, out) # un-escape escaped python code blocks out = regx_escp.sub(repl_escp, out) # make relative links absolute out = regx_rurl.sub(repl_rurl, out) # write HTML page fname = page.fname.replace(dir_in, dir_out) fname = re.sub(MKD_PATT, ".html", fname) with codecs.open(fname, 'w', opts.output_enc) as fp: fp.write(out) print("success: built project")