Skip to content

Commit

Permalink
Merge pull request #11 from anlutro/develop
Browse files Browse the repository at this point in the history
changes for 0.6
  • Loading branch information
anlutro authored Sep 18, 2020
2 parents dee3f1b + ddbe6fb commit 1ea25b8
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 68 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ resulting .html file will be named.

### Generating the blog

`russell generate` will create a file `run.py` which you can invoke to generate
your static site.
`russell generate` will run the `generate` function in your `config.py`, which
should contain all the instructions for generating HTML and other assets.

To test your newly generated site, run `russell serve`.

Expand Down
45 changes: 45 additions & 0 deletions example/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env python3

import os.path
import logging
import sass
import russell

root_path = os.path.dirname(__file__)
args = russell.get_cli_args()
logging.basicConfig(
level=logging.DEBUG if args.verbose else logging.INFO,
format="%(asctime)s %(levelname)8s [%(name)s] %(message)s",
)
blog = russell.BlogEngine(
root_path=root_path,
root_url=args.root_url or "//localhost",
site_title="Russell example",
site_desc=("An example Russell site."),
)

# add content
blog.add_pages()
blog.add_posts()


def generate():
# copy and generate assets
blog.copy_assets()
blog.write_file(
"assets/style.css",
sass.compile(filename=os.path.join(blog.root_path, "style.sass")),
)
blog.add_asset_hashes()

# generate HTML pages
blog.generate_index(num_posts=3)
blog.generate_archive()
blog.generate_pages()
blog.generate_posts()
blog.generate_tags()

# generate other stuff
blog.generate_sitemap(https=False)
blog.generate_rss()
blog.write_file("robots.txt", "User-agent: *\nDisallow:\n")
40 changes: 0 additions & 40 deletions example/run.py

This file was deleted.

1 change: 1 addition & 0 deletions russell/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from .__version__ import __version__
from russell.engine import BlogEngine
from russell.cli import get_args as get_cli_args
2 changes: 1 addition & 1 deletion russell/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.5.10"
__version__ = "0.6.0"
88 changes: 75 additions & 13 deletions russell/cli.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
import argparse
import datetime
import functools
import http.server
import importlib.machinery
import importlib.util
import os
import os.path
import re
import shutil
import subprocess

import dateutil.tz
import slugify

import russell
from russell.__version__ import __version__


def load_config_py(path=None):
if path is None:
path = os.getcwd() + "/config.py"
loader = importlib.machinery.SourceFileLoader("russell_config", path)
spec = importlib.util.spec_from_loader(loader.name, loader)
mod = importlib.util.module_from_spec(spec)
loader.exec_module(mod)
return mod


def setup(dest):
Expand Down Expand Up @@ -96,23 +111,61 @@ def publish(draft_file, update_pubdate=True):


def generate():
subprocess.check_call(["python", "run.py"])


def serve():
russell_config = load_config_py()
russell_config.generate()


class CustomHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
hash_pattern = re.compile(r"[0-9a-f]{8,12,16,24,32,48,64}")

def translate_path(self, path):
path = super().translate_path(path)
try_paths = []

directory, filename = path.rsplit("/", maxsplit=1)
if "." in filename:
file_parts = filename.split(".")
if (
len(file_parts) > 2
and len(file_parts[1]) % 8 == 0
and re.match(r"[0-9a-f]", file_parts[1])
):
del file_parts[1]
unbusted_path = "/".join([directory, ".".join(file_parts)])
try_paths.append(unbusted_path)
else:
try_paths.extend([path + ".html", path + "/index.html"])

for try_path in try_paths:
if os.path.exists(try_path):
return try_path
return path


def serve(dist_dir):
try:
subprocess.check_call(["sh", "-c", "cd dist && python -m http.server"])
httpd = http.server.HTTPServer(
("127.0.0.1", 8000),
functools.partial(CustomHTTPRequestHandler, directory=dist_dir),
)
sa = httpd.socket.getsockname()
print("Serving HTTP on http://%s:%s/ ..." % sa)
httpd.serve_forever()
except KeyboardInterrupt:
pass
finally:
httpd.server_close()


def get_parser():
parser = argparse.ArgumentParser("russell")
parser.add_argument("-v", "--verbose", action="store_true")
parser.add_argument("-r", "--root-path", default=os.getcwd())
parser.add_argument(
"-v",
"-V",
"--version",
action="version",
version="russell version " + str(russell.__version__),
version="russell version " + str(__version__),
)

cmd_subparsers = parser.add_subparsers(dest="command")
Expand All @@ -139,20 +192,29 @@ def get_parser():
)

generate_parser = cmd_subparsers.add_parser("generate")
generate_parser.add_argument("--root-url")

serve_parser = cmd_subparsers.add_parser("serve")
serve_parser.add_argument(
"-d", "--dist-dir", default=os.path.join(os.getcwd(), "dist")
)

return parser


def parse_args(parser=None, args=None):
parser = parser or get_parser()
return parser.parse_args(args)
_args = None


def get_args():
global _args
return _args


def main(args=None):
parser = get_parser()
args = parse_args(parser)
args = parser.parse_args()
global _args
_args = args

if not args.command:
return parser.print_help()
Expand All @@ -174,7 +236,7 @@ def main(args=None):
if args.command == "generate":
return generate()
if args.command == "serve":
return serve()
return serve(os.path.join(os.getcwd(), "dist"))


if __name__ == "__main__":
Expand Down
8 changes: 5 additions & 3 deletions russell/content.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
SYSTEM_TZINFO = dateutil.tz.tzlocal()


def render_markdown(md):
extensions = ["markdown.extensions.fenced_code"]
return markdown.markdown(md, extensions=extensions)
md = markdown.Markdown(extensions=["markdown.extensions.fenced_code"])


def render_markdown(text):
return md.convert(text)


def schema_url(url, https=False):
Expand Down
45 changes: 39 additions & 6 deletions russell/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,14 @@ class BlogEngine:
generating end results.
"""

def __init__(self, root_path, root_url, site_title, site_desc=None):
def __init__(
self,
root_path,
root_url,
site_title,
site_desc=None,
cache_busting_strategy="qs",
):
"""
Constructor.
Expand All @@ -56,7 +63,10 @@ def __init__(self, root_path, root_url, site_title, site_desc=None):
root_url (str): The root URL of your website.
site_title (str): The title of your website.
site_desc (str): A subtitle or description of your website.
cache_busting_strategy (str): None, "qs" or "part"
"""
assert os.path.exists(root_path), "root_path must be an existing directory"
assert root_url, "root_url must be set"
self.root_path = root_path
self.root_url = root_url
self.site_title = site_title
Expand All @@ -70,6 +80,13 @@ def __init__(self, root_path, root_url, site_title, site_desc=None):
self.tags = self.cm.tags

self.asset_hash = {}
if cache_busting_strategy == "qs":
self.get_asset_url = self.get_asset_url_qs
elif cache_busting_strategy == "part":
self.get_asset_url = self.get_asset_url_part
else:
LOG.warning("no cache busting will be used!")
self.get_asset_url = str

self.jinja = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.join(root_path, "templates")),
Expand All @@ -87,18 +104,34 @@ def __init__(self, root_path, root_url, site_title, site_desc=None):
}
)

def get_asset_url(self, path):
def get_asset_url_qs(self, path):
"""
Get the URL of an asset. If asset hashes are added and one exists for
the path, it will be appended as a query string.
Args:
path (str): Path to the file, relative to your "assets" directory.
"""
if path.endswith(self.bust_extensions) and path in self.asset_hash:
path += "?" + self.asset_hash[path]
return self.root_url + "/assets/" + path

bust_extensions = (".js", ".min.js", ".js.map", ".css", ".min.css", ".css.map")

def get_asset_url_part(self, path):
"""
Get the URL of an asset. If asset hashes are added and one exists for
the path, it will be appended as a query string.
Args:
path (str): Path to the file, relative to your "assets" directory.
"""
url = self.root_url + "/assets/" + path
if path in self.asset_hash:
url += "?" + self.asset_hash[path]
return url
if path.endswith(self.bust_extensions) and path in self.asset_hash:
*dirs, filename = path.split("/")
file_parts = filename.split(".", maxsplit=1)
file_parts.insert(1, self.asset_hash[path])
path = "/".join(dirs + [".".join(file_parts)])
return self.root_url + "/assets/" + path

def add_pages(self, path="pages"):
"""
Expand Down
5 changes: 3 additions & 2 deletions tests/engine_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ def test_get_asset_link(engine):


def test_get_asset_link_with_hash(engine):
engine.asset_hash["test.css"] = "asdf"
assert "//localhost/assets/test.css?asdf" == engine.get_asset_url("test.css")
engine.asset_hash["test.css"] = "0f4c"
assert "//localhost/assets/test.css?0f4c" == engine.get_asset_url_qs("test.css")
assert "//localhost/assets/test.0f4c.css" == engine.get_asset_url_part("test.css")


def test_get_posts_does_not_mutate_posts(engine):
Expand Down
2 changes: 1 addition & 1 deletion tests/functional_cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def russell_dir_variation(tmpdir, request):
def test_setup_new_and_existing(russell_dir_variation):
d = russell_dir_variation
russell.cli.setup(str(d))
assert d.join("run.py").check()
assert d.join("config.py").check()
assert d.join("requirements.txt").check()
assert d.join(".gitignore").check()
assert d.join("style.sass").check()
Expand Down

0 comments on commit 1ea25b8

Please sign in to comment.