M .travis.yml +3 -1
@@ 1,6 1,8 @@
language: python
python:
- - "2.7"
+ - "3.2"
+ - "3.3"
+ - "3.6"
install:
- "pip install markdown"
script:
M README.md +6 -6
@@ 32,7 32,7 @@ yours).
**You** should know Markdown and optionally Python if you want to use Poole's
dirty content generation capability.
-**Your system** should have installed Python ≥ 2.7 and [python-markdown][pymd].
+**Your system** should have installed Python ≥ 3.2 and [python-markdown][pymd].
Poole has been tested on Linux but should also work on other Unix systems and
Windows (in theory, [report an issue][issues] if it fails).
@@ 109,16 109,16 @@ Every Poole page is based on the skeleto
site layout means adjusting `page.html` and extending or replacing its CSS file
`input/poole.css`.
-The only thing you should keep in `page.html` are the embedded
-{{\_\_content\_\_}} and {{\_\_encoding\_\_}} expressions. Below is an almost
-minimal `page.html` file. It does not look nice but it's a clean starting point
-to build your own layout from scratch.
+The only thing you should keep in `page.html` is the embedded
+{{\_\_content\_\_}} expression. Below is an almost minimal `page.html` file.
+It does not look nice but it's a clean starting point to build your own layout
+from scratch.
Minimal `page.html`:
<html>
<head>
- <meta http-equiv="Content-Type" content="text/html; charset={{ __encoding__ }}" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf8" />
</head>
<body>
{{ __content__ }}
M poole.py +20 -32
@@ 23,7 23,6 @@
#
# =============================================================================
-import codecs
import glob
import imp
import optparse
@@ 32,16 31,18 @@ from os.path import join as opj
from os.path import exists as opx
import re
import shutil
-import StringIO
+import io
import sys
import traceback
-import urlparse
+import urllib.parse
-from SimpleHTTPServer import SimpleHTTPRequestHandler
-from BaseHTTPServer import HTTPServer
+from http.server import SimpleHTTPRequestHandler
+from http.server import HTTPServer
import markdown
+UTF8 = 'UTF8'
+
HERE = os.path.dirname(os.path.realpath(__file__))
THEME_DIR = opj(HERE, 'themes')
@@ 61,7 62,7 @@ EXAMPLE_FILES = {
"page.html": """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
- <meta http-equiv="Content-Type" content="text/html; charset={{ __encoding__ }}" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf8" />
<title>poole - {{ hx(page["title"]) }}</title>
<meta name="description" content="{{ hx(page.get("description", "a poole site")) }}" />
<meta name="keywords" content="{{ hx(page.get("keywords", "poole")) }}" />
@@ 204,7 205,7 @@ posts = [p for p in pages if "post" in p
posts.sort(key=lambda p: p.get("date"), reverse=True) # sort post pages by date
for p in posts:
date = datetime.strptime(p.date, "%Y-%m-%d").strftime("%B %d, %Y")
- print " * **[%s](%s)** - %s" % (p.post, p.url, date) # markdown list item
+ print(" * **[%s](%s)** - %s" % (p.post, p.url, date)) # markdown list item
%-->
Have a look into `input/blog.md` to see how it works. Feel free to adjust it
@@ 221,7 222,7 @@ opj("input", "blog.2013-04-08.Lorem_Ipsu
*Posted at
<!--%
from datetime import datetime
-print datetime.strptime(page["date"], "%Y-%m-%d").strftime("%B %d, %Y")
+print(datetime.strptime(page["date"], "%Y-%m-%d").strftime("%B %d, %Y"))
%-->*
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sed pretium arcu.
@@ 276,7 277,7 @@ She looks like one. Why do you think tha
her forward!
[Ni!](http://chrisvalleskey.com/fillerama/)
-""",
+"""
}
def init(project, theme):
@@ 297,7 298,7 @@ def init(project, theme):
for fname, content in EXAMPLE_FILES.items():
print('info: create example %r' % fname)
- with open(opj(project, fname), 'w') as fp:
+ with open(opj(project, fname), 'w', encoding=UTF8) as fp:
fp.write(content)
if theme != 'minimal':
@@ 385,7 386,7 @@ class Page(dict):
if virtual:
self.raw = virtual
else:
- with codecs.open(fname, 'r', self._opts.input_enc) as fp:
+ with open(fname, 'r', encoding=UTF8) as fp:
self.raw = fp.readlines()
# split raw content into macro definitions and real content
@@ 452,7 453,7 @@ def build(project, opts):
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))
+ repl_rurl = lambda m: urllib.parse.urljoin(opts.base_url, m.group(1))
regx_eval = re.compile(r'(?<!\\)(?:(?:<!--|{){)(.*?)(?:}(?:-->|}))', re.S)
@@ 465,11 466,7 @@ def build(project, opts):
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
+ return str(repl)
regx_exec = re.compile(r'(?<!\\)(?:(?:<!--|{)%)(.*?)(?:%(?:-->|}))', re.S)
@@ 484,17 481,15 @@ def build(project, opts):
stmt = ind_rex.sub('', stmt)
# execute
- sys.stdout = StringIO.StringIO()
+ sys.stdout = io.StringIO()
try:
- exec stmt in macros.copy()
+ exec(stmt, 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
# -------------------------------------------------------------------------
@@ 526,7 521,6 @@ def build(project, opts):
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
@@ 547,7 541,7 @@ def build(project, opts):
pages = []
custom_converter = macros.get('converter', {})
- for cwd, dirs, files in os.walk(dir_in.decode(opts.filename_enc)):
+ for cwd, dirs, files in os.walk(dir_in):
cwd_site = cwd[len(dir_in):].lstrip(os.path.sep)
if not opts.dry_run:
for sdir in dirs[:]:
@@ 622,14 616,14 @@ def build(project, opts):
# render complete HTML pages
# -------------------------------------------------------------------------
- with codecs.open(opj(project, "page.html"), 'r', opts.input_enc) as fp:
+ with open(opj(project, "page.html"), 'r', encoding=UTF8) 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:
+ with open(fname, 'r', opts.input_enc) as fp:
template = fp.read()
else:
template = default_template
@@ 652,7 646,7 @@ def build(project, opts):
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:
+ with open(fname, 'w', encoding=UTF8) as fp:
fp.write(out)
if opts.dry_run:
@@ 712,12 706,6 @@ def options():
help="input files to ignore (default: '^\.|~$')")
og.add_option("" , "--md-ext", default=[], metavar="EXT",
action="append", help="enable a markdown extension")
- og.add_option("", "--input-enc", default="utf-8", metavar="ENC",
- help="encoding of input pages (default: utf-8)")
- og.add_option("", "--output-enc", default="utf-8", metavar="ENC",
- help="encoding of output pages (default: utf-8)")
- og.add_option("", "--filename-enc", default="utf-8", metavar="ENC",
- help="encoding of file names (default: utf-8)")
og.add_option("" , "--dry-run", action="store_true", default=False,
help="go through the rendering process without actually "
"outputting/deleting any files")
M tests/expected/input/blog.2013-04-08.Lorem_Ipsum.md +1 -1
@@ 6,7 6,7 @@
*Posted at
<!--%
from datetime import datetime
-print datetime.strptime(page["date"], "%Y-%m-%d").strftime("%B %d, %Y")
+print(datetime.strptime(page["date"], "%Y-%m-%d").strftime("%B %d, %Y"))
%-->*
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sed pretium arcu.
M tests/expected/input/blog.md +1 -1
@@ 12,7 12,7 @@ posts = [p for p in pages if "post" in p
posts.sort(key=lambda p: p.get("date"), reverse=True) # sort post pages by date
for p in posts:
date = datetime.strptime(p.date, "%Y-%m-%d").strftime("%B %d, %Y")
- print " * **[%s](%s)** - %s" % (p.post, p.url, date) # markdown list item
+ print(" * **[%s](%s)** - %s" % (p.post, p.url, date)) # markdown list item
%-->
Have a look into `input/blog.md` to see how it works. Feel free to adjust it
M tests/expected/output/blog.2013-04-01.Holy_Grail.html +1 -1
@@ 1,7 1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf8" />
<title>poole - blog</title>
<meta name="description" content="a poole site" />
<meta name="keywords" content="poole" />
M tests/expected/output/blog.2013-04-08.Lorem_Ipsum.html +1 -1
@@ 1,7 1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf8" />
<title>poole - blog</title>
<meta name="description" content="a poole site" />
<meta name="keywords" content="poole" />
M tests/expected/output/blog.html +1 -1
@@ 1,7 1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf8" />
<title>poole - blog</title>
<meta name="description" content="a poole site" />
<meta name="keywords" content="poole" />
M tests/expected/output/index.html +1 -1
@@ 1,7 1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf8" />
<title>poole - home</title>
<meta name="description" content="a poole site" />
<meta name="keywords" content="poole" />
M tests/expected/output/layout.html +1 -1
@@ 1,7 1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf8" />
<title>poole - layout</title>
<meta name="description" content="a poole site" />
<meta name="keywords" content="poole" />
M tests/expected/output/logic.html +1 -1
@@ 1,7 1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf8" />
<title>poole - logic</title>
<meta name="description" content="a poole site" />
<meta name="keywords" content="poole" />
M tests/expected/page.html +1 -1
@@ 1,7 1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
- <meta http-equiv="Content-Type" content="text/html; charset={{ __encoding__ }}" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf8" />
<title>poole - {{ hx(page["title"]) }}</title>
<meta name="description" content="{{ hx(page.get("description", "a poole site")) }}" />
<meta name="keywords" content="{{ hx(page.get("keywords", "poole")) }}" />
M tests/run.py +2 -3
@@ 1,6 1,5 @@
#!/usr/bin/env python
-import codecs
import os
import shutil
import subprocess
@@ 49,8 48,8 @@ if generated == []:
p = subprocess.Popen(cmd_diff, stdout=subprocess.PIPE)
diff = p.communicate()[0]
if diff:
- with codecs.open(ERRORS, 'w', 'UTF8') as fp:
- fp.write(diff.decode('UTF8'))
+ with open(ERRORS, 'wb') as fp:
+ fp.write(diff)
print("failed - see %s for details" % ERRORS)
sys.exit(1)