Перенос моделей Markdown и YAML из resources в models

#1
master
Антон Касимов 2019-02-16 20:05:09 +03:00
parent 11be11eadc
commit 1013ff5618
5 changed files with 174 additions and 157 deletions

View File

@ -1,11 +1,11 @@
from pathlib import Path
from pyramid.config import Configurator
from pyramid.path import AssetResolver
from .resources import Root
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from pathlib import Path
from flatfilecms.resources import Root
class PagesEventHandler(FileSystemEventHandler):
@ -54,10 +54,10 @@ def main(global_config, **settings):
watchdog = asbool(settings.get(
'watchdog', 'false'))
settings['watchdog'] = watchdog
settings.setdefault('pages_dir', 'pages')
settings.setdefault('data_dir', 'data')
settings.setdefault('pages_path', 'pages')
settings.setdefault('data_path', 'data')
pages = Root(settings['pages_dir'])
pages = Root(settings['pages_path'], settings['data_path'])
def factory(request):
return pages
@ -69,7 +69,7 @@ def main(global_config, **settings):
config.scan()
if watchdog:
path = AssetResolver().resolve(
settings['pages_dir']).abspath()
settings['pages_path']).abspath()
observer = Observer()
event_handler = PagesEventHandler(pages, path)
observer.schedule(

140
flatfilecms/models.py Normal file
View File

@ -0,0 +1,140 @@
from pyramid.path import AssetResolver
from pathlib import PurePath
import yaml
import frontmatter
import markdown
from zope.interface import (Interface, implementer)
from flatfilecms.filters import IgnoreExtension
class Loader(yaml.Loader):
def __init__(self, stream):
super(Loader, self).__init__(stream)
Loader.add_constructor('!include', Loader.include)
Loader.add_constructor('!markdown', Loader.markdown)
def include(self, node):
if isinstance(node, yaml.ScalarNode):
return self.extractFile(self.construct_scalar(node))
elif isinstance(node, yaml.SequenceNode):
return [self.extractFile(filename)
for filename in self.construct_sequence(node)]
elif isinstance(node, yaml.MappingNode):
result = {}
for k, v in self.construct_mapping(node).items():
result[k] = self.extractFile(v)
return result
else:
raise yaml.constructor.ConstructorError(
"Error:: unrecognised node type in !include statement")
def markdown(self, node):
if not isinstance(node, yaml.ScalarNode):
raise yaml.constructor.ConstructorError(
"Error:: unrecognised node type in !markdown statement")
m = self.construct_scalar(node)
return markdown.markdown(
m,
extensions=[IgnoreExtension(), 'markdown.extensions.extra'],
output_format='html5',
tab_length=2)
def extractFile(self, filename):
path = PurePath(self.data_path) / filename
f = AssetResolver().resolve(str(path)).stream()
if f:
if path.suffix in ['.yaml', '.yml', '.json']:
return yaml.load(f, Loader)
return f.read().decode()
def LoaderFactory(data_path):
cl = Loader
cl.data_path = data_path
return cl
class CustomYAMLHandler(frontmatter.YAMLHandler):
def __init__(self, data_path):
self.loader = LoaderFactory(data_path)
def load(self, fm, **kwargs):
return yaml.load(fm, self.loader)
class Folder(dict):
def __init__(self, name, parent, path):
self.path = path
self.name = name
self.parent = parent
if parent is not None:
self.url = "f{parent.url}/{name}"
self.data_path = parent.data_path
else:
self.url = ''
for entry in AssetResolver().resolve(path).listdir():
asset = f"{path}/{entry}"
if AssetResolver().resolve(asset).isdir():
self.create_dir(asset)
else:
self.create_file(asset)
def create_file(self, asset):
path = PurePath(asset)
if path.suffix == '.md':
self[path.stem] = Markdown(path.stem, self, asset)
elif path.suffix == '.yaml':
self[path.stem] = YAML(path.stem, self, asset)
elif path.suffix == '.j2' or path.suffix == '.jinja2':
self[path.stem] = Jinja2(path.stem, self, asset)
else:
name = path.name
# Если имя файла не index.html,
# то отдавать по имени файла
if name == 'index.html':
name = 'index'
self[name] = Document(name, self, asset)
def create_dir(self, asset):
path = PurePath(asset)
self[path.name] = Folder(path.name, self, asset)
class Document(object):
def __init__(self, name, parent, path):
self.name = name
self.parent = parent
self.path = path
self.url = f"{parent.url}/{name}"
self.data_path = parent.data_path
class IMarkdown(Interface):
""" Интерфейс-маркер для генератора markdown """
@implementer(IMarkdown)
class Markdown(Document):
def __init__(self, name, parent, path):
super(Markdown, self).__init__('', parent, path)
self.page = frontmatter.load(
AssetResolver().resolve(self.path).stream(),
handler=CustomYAMLHandler(self.data_path),
).to_dict()
@implementer(IMarkdown)
class YAML(Document):
def __init__(self, name, parent, path):
super(YAML, self).__init__('', parent, path)
self.page = yaml.load(
AssetResolver().resolve(self.path).stream(),
LoaderFactory(self.data_path))
class Jinja2(Document):
pass

View File

@ -1,5 +1,4 @@
from pyramid.path import AssetResolver
from pathlib import PurePath
from flatfilecms.models import Folder
def flat(d, path):
@ -15,61 +14,10 @@ def flat(d, path):
return structure
class Folder(dict):
def __init__(self, name, parent, path):
self.__path__ = path
self.__name__ = name
self.__parent__ = parent
for entry in AssetResolver().resolve(path).listdir():
asset = f"{path}/{entry}"
if AssetResolver().resolve(asset).isdir():
self.create_dir(asset)
else:
self.create_file(asset)
def create_file(self, asset):
path = PurePath(asset)
if path.suffix == '.md':
self[path.stem] = Markdown(path.stem, self, asset)
elif path.suffix == '.yaml':
self[path.stem] = YAML(path.stem, self, asset)
elif path.suffix == '.j2' or path.suffix == '.jinja2':
self[path.stem] = Jinja2(path.stem, self, asset)
else:
name = path.name
# Если имя файла не index.html,
# то отдавать по имени файла
if name == 'index.html':
name = 'index'
self[name] = Document(name, self, asset)
def create_dir(self, asset):
path = PurePath(asset)
self[path.name] = Folder(path.name, self, asset)
class Root(Folder):
def __init__(self, path, data_path):
self.data_path = data_path
super(Root, self).__init__('', None, path)
def structure(self, base_dir=''):
return flat(self, base_dir)
class Root(Folder):
def __init__(self, path):
super(Root, self).__init__('', None, path)
class Document(object):
def __init__(self, name, parent, path):
self.__name__ = name
self.__parent__ = parent
self.__path__ = path
class Markdown(Document):
pass
class YAML(Document):
pass
class Jinja2(Document):
pass

View File

@ -7,70 +7,14 @@ from pyramid.httpexceptions import (HTTPFound, HTTPNotFound)
from pyramid.response import FileResponse
from pyramid.path import AssetResolver
from pathlib import PurePath
from ..resources import (Folder, Document, Markdown, YAML, Jinja2)
import yaml
import frontmatter
import markdown
from ..filters import IgnoreExtension
class Loader(yaml.Loader):
def __init__(self, stream):
super(Loader, self).__init__(stream)
Loader.add_constructor('!include', Loader.include)
Loader.add_constructor('!markdown', Loader.markdown)
def include(self, node):
if isinstance(node, yaml.ScalarNode):
return self.extractFile(self.construct_scalar(node))
elif isinstance(node, yaml.SequenceNode):
return [self.extractFile(filename)
for filename in self.construct_sequence(node)]
elif isinstance(node, yaml.MappingNode):
result = {}
for k, v in self.construct_mapping(node).items():
result[k] = self.extractFile(v)
return result
else:
raise yaml.constructor.ConstructorError(
"Error:: unrecognised node type in !include statement")
def markdown(self, node):
if not isinstance(node, yaml.ScalarNode):
raise yaml.constructor.ConstructorError(
"Error:: unrecognised node type in !markdown statement")
m = self.construct_scalar(node)
return markdown.markdown(
m,
extensions=[IgnoreExtension(), 'markdown.extensions.extra'],
output_format='html5',
tab_length=2)
def extractFile(self, filename):
path = PurePath(self.data_dir) / filename
f = AssetResolver().resolve(str(path)).stream()
if f:
if path.suffix in ['.yaml', '.yml', '.json']:
return yaml.load(f, Loader)
return f.read().decode()
def LoaderFactory(data_dir):
cl = Loader
cl.data_dir = data_dir
return cl
class CustomYAMLHandler(frontmatter.YAMLHandler):
def __init__(self, data_dir):
self.loader = LoaderFactory(data_dir)
def load(self, fm, **kwargs):
return yaml.load(fm, self.loader)
from flatfilecms.models import (
Folder,
Document,
IMarkdown,
Jinja2,
LoaderFactory)
class PagesView:
@ -87,45 +31,30 @@ class PagesView:
@view_config(context=Document)
def document(self):
return FileResponse(
AssetResolver().resolve(self.context.__path__).abspath(),
AssetResolver().resolve(self.context.path).abspath(),
request=self.request)
@view_config(context=IMarkdown)
def process_yaml(self):
if 'redirect' in self.post:
return HTTPFound(location=self.post.redirect)
if 'menu' not in self.post:
self.post['menu'] = yaml.load(
post = self.context.page
if 'redirect' in post:
return HTTPFound(location=post['redirect'])
if 'menu' not in post:
post['menu'] = yaml.load(
AssetResolver().resolve(
str(PurePath(self.request.registry.settings['data_dir']) /
'menu/default.yaml')).stream(),
LoaderFactory(self.request.registry.settings['data_dir']))
str(PurePath(self.context.data_path) /
'menu/default.yaml')).stream(),
LoaderFactory(self.context.data_path))
response = render_to_response(
'{0}.jinja2'.format(
self.post.get('template', 'default')),
self.post,
post.get('template', 'default')),
post,
request=self.request
)
if 'content_type' in self.post:
response.content_type = self.post['content_type']
if 'content_type' in post:
response.content_type = post['content_type']
return response
@view_config(context=YAML)
def yaml(self):
self.post = yaml.load(
AssetResolver().resolve(
self.context.__path__).stream(),
LoaderFactory(self.request.registry.settings['data_dir']))
return self.process_yaml()
@view_config(context=Markdown)
def markdown(self):
self.post = frontmatter.load(
AssetResolver().resolve(
self.context.__path__).stream(),
handler=CustomYAMLHandler(self.request.registry.settings['data_dir']),
).to_dict()
return self.process_yaml()
@view_config(context=Jinja2)
def jinja2(self):
return render_to_response(

View File

@ -4,7 +4,7 @@ import pytest
@pytest.fixture
def pages():
from flatfilecms.resources import Root
return Root('../tests/pages')
return Root('../tests/pages', '../tests/data')
def test_loading(pages):
@ -12,5 +12,5 @@ def test_loading(pages):
def test_db_contains_pages(pages):
assert pages['index'].title == 'Заглушка для тестов БД'
assert pages['index'].page['title'] == 'Заглушка для тестов БД'
pass