Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
* Replce MetaFile with FSFile
* Plugin cleanup
* Add extends support to Jinja
* Cleanup
  • Loading branch information
mayo committed Dec 26, 2017
1 parent af7108e commit 9ac90d8
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 84 deletions.
108 changes: 50 additions & 58 deletions hana/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@

from hana import errors

#TODO: build in file watcher, server...
#TODO: setup logging
#TODO: plugins directory to put on path for ad-hoc plugins?

class Hana():

Expand All @@ -28,11 +25,6 @@ def __init__(self, configuration=None, output_directory=None):

self.files = FileSet()

@property
def files(self):
#TODO: change to file manager
return MetaFile._all_files

def _load_configuration(config_file):
#TODO: load config
pass
Expand Down Expand Up @@ -104,7 +96,6 @@ def makedirs(path, dir):

#TODO: fileset to make filtering more easier. Filter files based on glob pattern, so in plugins:
#files.filter(glob) returns iterator/generator with matches
#TODO: support dictionary/list-like iteration so behave like current implementation
#TODO: this is not ideal and error prone, as it sub-calls .add, etc. This should really return a proxy that's smart enough to iterate through matching items, rather than recursively call action methods.
class FileSet():

Expand Down Expand Up @@ -134,7 +125,6 @@ def filter(self, patterns):
"""Return FileSet with subset of files
"""


if not patterns:
return self

Expand All @@ -148,15 +138,13 @@ def filter(self, patterns):

return fm

#def push
#TODO: verify base path, resolve relative paths
def add(self, filename, f):
self._files[filename] = f

if self._parent:
self._parent.add(filename, f)

#def pop
#TODO: verify base path, resolve relative paths
def remove(self, filename):
self._files.pop(filename)
Expand All @@ -178,62 +166,33 @@ def __init__(self, file_manager, patterns):



#TODO: create source-less file class that has loaded set to true to avoid loading

# NEW NOTES AS OF Sept 13
# Initially, MetaFile that is source file backed will have filename set to source_file.
#
class File(dict):

def __init__(self, *args, **kwargs):
self._is_binary = None
self.loaded = False

if 'content' in kwargs:
self.loaded = True

super(File, self).update(*args, **kwargs)

def __getitem__(self, key):
if key == 'contents' and not self.loaded:
self._get_contents()

return super(File, self).__getitem__(key)

def __repr__(self):
return '<%s %s>' % (self.__class__.__name__, dict(self))

@property
def is_binary(self):
#TODO: if source file is not defined, but we have content, use that instead of reading a file. If neither exists, treat it as binary
#NOTE: once using contents, this should not be cached as contents can change on the fly
if self._is_binary is None:

with open(self.source_file, 'rb') as fin:
CHUNKSIZE = 1024

while True:
chunk = fin.read(CHUNKSIZE)
if '\0' in chunk:
self._is_binary = True
break

if len(chunk) < CHUNKSIZE:
break

self._is_binary = False

return self._is_binary
# If we don't have any content, treat it as binary and try to do detection later
if not self['contents']:
return True

return '\0' in self['contents']

def update(self, *args, **kwargs):
if args:
if len(args) > 1:
raise TypeError("update expected at most 1 arguments, "
"got %d" % len(args))

other = dict(args[0])

for key in other:
self[key] = other[key]

for key in kwargs:
self[key] = kwargs[key]

Expand All @@ -243,25 +202,58 @@ def setdefault(self, key, value=None):
return self[key]


#TODO: clean this up with File
class MetaFile(File):
class FSFile(File):

def __init__(self, filename, *args, **kwargs):
super(MetaFile, self).__init__(*args, **kwargs)
super(FSFile, self).__init__(*args, **kwargs)

if not filename:
raise

self._is_binary = None
self.loaded = False
self.filename = filename

def __getitem__(self, key):
if key == 'contents' and not self.loaded:
self._get_contents()

return super(File, self).__getitem__(key)

#Keep track of source file
self.source_file = filename
# Override is_binary to avoid loading files unnecesarily
@property
def is_binary(self):
# If the file is already loaded, it may have been processed
if self.loaded:
self._is_binary = None
return '\0' in self['contents']

if self._is_binary is None:
with open(self.filename, 'rb') as fin:
CHUNKSIZE = 1024

while True:
chunk = fin.read(CHUNKSIZE)
if '\0' in chunk:
self._is_binary = True
break

if len(chunk) < CHUNKSIZE:
break

self._is_binary = False

return self._is_binary

#TODO: this is called from __getitem__
def _get_contents(self):
#TODO: raise if 'filename' is not defined
#print 'r%s %s' % ('b' if self.is_binary else 't', self['filename'])
if self.is_binary:
self['contents'] = open(self.source_file, 'r').read()
self['contents'] = open(self.filename, 'rb').read()

else:
self['contents'] = codecs.open(self.source_file, 'r', 'utf-8').read()
self['contents'] = codecs.open(self.filename, 'r', 'utf-8').read()

self.loaded = True

def __repr__(self):
return '<%s %s %s>' % (self.__class__.__name__, self.source_file, dict(self))
return '<%s %s %s>' % (self.__class__.__name__, self.filename, dict(self))
78 changes: 52 additions & 26 deletions hana/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

# FileLoader plugin

from hana.core import MetaFile
from hana.core import FSFile
import pathspec

class FileLoader():
def __init__(self, source_path, ignore_patterns=[]):
def __init__(self, source_path, ignore_patterns=[], source_file_keyword=None):
self._source_path = source_path
self.ignore_patterns = ignore_patterns
self.source_file_keyword = source_file_keyword

if not os.path.isdir(self._source_path):
raise SourceDirectoryError()
Expand All @@ -30,7 +31,12 @@ def __call__(self, files, hana):
if filepath in files:
raise FileExistsError("File {} already exists".format(filepath))

files.add(filepath, MetaFile(source, _source_file=source))
metadata = {}

if self.source_file_keyword:
metadata[self.source_file_keyword] = source

files.add(filepath, FSFile(source, **metadata))

class FileLoaderError(HanaPluginError): pass
class FileExistsError(FileLoaderError): pass
Expand Down Expand Up @@ -261,34 +267,30 @@ def excerpt(files, hana):
f['excerpt'] = p


# Branch

import pathspec

class Branch():

def __init__(self, qualifier):
if isinstance(qualifier, str):
qualifier = [qualifier]

if callable(filter):
#TODO
pass
# Jinja

self.match = pathspec.PathSpec.from_lines('gitwildmatch', qualifier).match_file
from jinja2 import evalcontextfilter, Environment, FileSystemLoader, Markup
from jinja2.ext import Extension
import jinja2.exceptions

self.plugins = []
# tojson filter in jinja sucks
import json
@evalcontextfilter
def jinja_json(eval_ctx, value, indent=None):
policies = eval_ctx.environment.policies
dumper = policies['json.dumps_function']
options = policies['json.dumps_kwargs']

def __call__(self, files, hana):
#TODO
pass
if indent is not None:
options = dict(options)
options['indent'] = indent

if dumper is None:
dumper = json.dumps

# Jinja
rv = dumper(value, **options)

from jinja2 import Environment, FileSystemLoader
from jinja2.ext import Extension
import jinja2.exceptions
return Markup(rv)

class JinjaTemplate():
def __init__(self, config={}):
Expand All @@ -303,12 +305,21 @@ def __init__(self, config={}):
'jinja2.ext.loopcontrols',
]

self.config = {
'extends_block': None,
}

self.config.update(config)

self.env = Environment(loader=FileSystemLoader(tplPath),
extensions=extensions)

self.env.trim_blocks = True
self.env.lstrip_blocks = True

#TODO: adding custom filters
self.env.filters['json'] = jinja_json

#try:
# from typogrify.templatetags import jinja_filters
#except ImportError:
Expand All @@ -325,6 +336,21 @@ def __call__(self, files, hana):
if 'index.' in filename:
print 'jinja ', filename

#TODO: this is not the nicest way of doing things... jekyll need it?
if 'extends' in f and not 'template' in f:
block_name = None

if 'extends-block' in f:
block_name = f['extends-block']
elif self.config['extends-block']:
block_name = self.config['extends-block']
else:
raise Exception('missing extends block')

f['template'] = True
f['contents'] = "{{% extends '{:s}' %}}{{% block {:s} %}}{:s}{{% endblock %}}".format(f['extends'], block_name, f['contents'])


if 'template' in f:
c = self.render(filename, f, hana)
f['contents'] = unicode(c)
Expand All @@ -342,7 +368,7 @@ def render(self, filename, f, hana):
try:
return template.render(site=hana.metadata, page=f)
except jinja2.exceptions.UndefinedError as e:
print filename
print 'Jinja Debug', filename
print f
raise

Expand Down

0 comments on commit 9ac90d8

Please sign in to comment.