Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ jobs:
run: |
pip install -e '.[test]'
- name: Run tests
run: pytest
run: |
pytest . --doctest-modules --doctest-glob "README.md"
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ This is a small package to load and parse files (or just text) with YAML front m
## Usage:

```python
import frontmatter
>>> import frontmatter

```

Load a post from a filename:

```python
post = frontmatter.load('tests/yaml/hello-world.txt')
>>> post = frontmatter.load('tests/yaml/hello-world.txt')

```

Or a file (or file-like object):
Expand Down
2 changes: 0 additions & 2 deletions docs/handlers.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
Customizing input and output
============================

.. module:: frontmatter

.. automodule:: frontmatter.default_handlers

.. autoclass:: frontmatter.default_handlers.BaseHandler
Expand Down
Empty file added examples/__init__.py
Empty file.
28 changes: 28 additions & 0 deletions examples/content/reversed.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
This is txt format to prevent reformatting
============================================

Dextra tempore deus
-------------------

Lorem markdownum est dicere pariter es dat si non, praesignis Styge, non
Maenalon magnae miserrimus. Corpora frustra committere insuetum et fecit
**Hippothousque arbore solio** inopem utraque concepit illa comantem me mortis
epulis protinus putares! Piceis *manibus*. Erinys et parum morsusque repugnat
ore corna sacris, pollice movet currus gestamina.

Genitoris forti circumfuso videbit fertur vulnere simillima
-----------------------------------------------------------

Audit enim, est illa nervis loco inque hoc, et rigido! Monstris vatibus laetos
contemptor Calydonia. Et visa capillo referens regia: usus: odiique nostro.
**Vim** sensit inpulit virginis metuens secum cogit, corpus.

Humus ater Dromas est honorem, Titanida glandibus sinit, e terras capillos
cremet retinentibus male. Tertia et cedit eliso flectere haec, cute nihil
marmore armo. Mihi [Olympi](http://que.org/saepepoenas), iam sustinet addidit
humana similis.

---
title: Front matter, reversed
ref: https://github.com/eyeseast/python-frontmatter/issues/67
---
76 changes: 76 additions & 0 deletions examples/reversed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import frontmatter

POST_TEMPLATE = """\
{content}

{start_delimiter}
{metadata}
{end_delimiter}
"""


class ReverseYAMLHandler(frontmatter.YAMLHandler):
"""
This is an example of using Handler.parse and Handler.format to move Frontmatter to the bottom
of a file, both for parsing and output.

>>> with open("examples/content/reversed.txt") as f:
... text = f.read()
>>> handler = ReverseYAMLHandler()
>>> post = frontmatter.loads(text, handler=handler)
>>> print(post['title'])
Front matter, reversed
>>> print(post['ref'])
https://github.com/eyeseast/python-frontmatter/issues/67
>>> print(frontmatter.dumps(post, handler=handler))
This is txt format to prevent reformatting
============================================
<BLANKLINE>
Dextra tempore deus
-------------------
<BLANKLINE>
Lorem markdownum est dicere pariter es dat si non, praesignis Styge, non
Maenalon magnae miserrimus. Corpora frustra committere insuetum et fecit
**Hippothousque arbore solio** inopem utraque concepit illa comantem me mortis
epulis protinus putares! Piceis *manibus*. Erinys et parum morsusque repugnat
ore corna sacris, pollice movet currus gestamina.
<BLANKLINE>
Genitoris forti circumfuso videbit fertur vulnere simillima
-----------------------------------------------------------
<BLANKLINE>
Audit enim, est illa nervis loco inque hoc, et rigido! Monstris vatibus laetos
contemptor Calydonia. Et visa capillo referens regia: usus: odiique nostro.
**Vim** sensit inpulit virginis metuens secum cogit, corpus.
<BLANKLINE>
Humus ater Dromas est honorem, Titanida glandibus sinit, e terras capillos
cremet retinentibus male. Tertia et cedit eliso flectere haec, cute nihil
marmore armo. Mihi [Olympi](http://que.org/saepepoenas), iam sustinet addidit
humana similis.
<BLANKLINE>
---
ref: https://github.com/eyeseast/python-frontmatter/issues/67
title: Front matter, reversed
---
"""

# FM_BOUNDARY as a string, so we can rsplit
FM_BOUNDARY = "---"

def split(self, text):
"""
Split text into frontmatter and content
"""
content, fm, _ = text.rsplit(self.FM_BOUNDARY, 2)
return fm, content

def format(self, post, **kwargs):
start_delimiter = kwargs.pop("start_delimiter", self.START_DELIMITER)
end_delimiter = kwargs.pop("end_delimiter", self.END_DELIMITER)
metadata = self.export(post.metadata, **kwargs)

return POST_TEMPLATE.format(
content=post.content,
metadata=metadata,
start_delimiter=start_delimiter,
end_delimiter=end_delimiter,
).strip()
47 changes: 15 additions & 32 deletions frontmatter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,6 @@

__all__ = ["parse", "load", "loads", "dump", "dumps"]

POST_TEMPLATE = """\
{start_delimiter}
{metadata}
{end_delimiter}

{content}
"""

# global handlers
handlers = {
Expand Down Expand Up @@ -57,11 +50,11 @@ def parse(text, encoding="utf-8", handler=None, **defaults):

.. testsetup:: *

import frontmatter
>>> import frontmatter

.. doctest::

>>> with open('../tests/yaml/hello-world.txt') as f:
>>> with open('tests/yaml/hello-world.txt') as f:
... metadata, content = frontmatter.parse(f.read())
>>> print(metadata['title'])
Hello, world!
Expand Down Expand Up @@ -102,7 +95,7 @@ def check(fd, encoding="utf-8"):

.. doctest::

>>> frontmatter.check('../tests/yaml/hello-world.txt')
>>> frontmatter.check('tests/yaml/hello-world.txt')
True

"""
Expand All @@ -125,7 +118,7 @@ def checks(text, encoding="utf-8"):

.. doctest::

>>> with open('../tests/yaml/hello-world.txt') as f:
>>> with open('tests/yaml/hello-world.txt') as f:
... frontmatter.checks(f.read())
True

Expand All @@ -141,8 +134,8 @@ def load(fd, encoding="utf-8", handler=None, **defaults):

.. doctest::

>>> post = frontmatter.load('../tests/yaml/hello-world.txt')
>>> with open('../tests/yaml/hello-world.txt') as f:
>>> post = frontmatter.load('tests/yaml/hello-world.txt')
>>> with open('tests/yaml/hello-world.txt') as f:
... post = frontmatter.load(f)

"""
Expand All @@ -163,7 +156,7 @@ def loads(text, encoding="utf-8", handler=None, **defaults):

.. doctest::

>>> with open('../tests/yaml/hello-world.txt') as f:
>>> with open('tests/yaml/hello-world.txt') as f:
... post = frontmatter.loads(f.read())

"""
Expand All @@ -181,22 +174,22 @@ def dump(post, fd, encoding="utf-8", handler=None, **kwargs):
::

>>> from io import BytesIO
>>> post = frontmatter.load('../tests/yaml/hello-world.txt')
>>> post = frontmatter.load('tests/yaml/hello-world.txt')
>>> f = BytesIO()
>>> frontmatter.dump(post, f)
>>> print(f.getvalue().decode('utf-8'))
---
layout: post
title: Hello, world!
---

<BLANKLINE>
Well, hello there, world.


.. testcode::

from io import BytesIO
post = frontmatter.load('../tests/yaml/hello-world.txt')
post = frontmatter.load('tests/yaml/hello-world.txt')
f = BytesIO()
frontmatter.dump(post, f)
print(f.getvalue().decode('utf-8'))
Expand All @@ -207,7 +200,7 @@ def dump(post, fd, encoding="utf-8", handler=None, **kwargs):
layout: post
title: Hello, world!
---

<BLANKLINE>
Well, hello there, world.

"""
Expand All @@ -232,18 +225,18 @@ def dumps(post, handler=None, **kwargs):

::

>>> post = frontmatter.load('../tests/yaml/hello-world.txt')
>>> post = frontmatter.load('tests/yaml/hello-world.txt')
>>> print(frontmatter.dumps(post)) # doctest: +NORMALIZE_WHITESPACE
---
layout: post
title: Hello, world!
---

<BLANKLINE>
Well, hello there, world.

.. testcode::

post = frontmatter.load('../tests/yaml/hello-world.txt')
post = frontmatter.load('tests/yaml/hello-world.txt')
print(frontmatter.dumps(post))

.. testoutput::
Expand All @@ -259,17 +252,7 @@ def dumps(post, handler=None, **kwargs):
if handler is None:
handler = getattr(post, "handler", None) or YAMLHandler()

start_delimiter = kwargs.pop("start_delimiter", handler.START_DELIMITER)
end_delimiter = kwargs.pop("end_delimiter", handler.END_DELIMITER)

metadata = handler.export(post.metadata, **kwargs)

return POST_TEMPLATE.format(
metadata=metadata,
content=post.content,
start_delimiter=start_delimiter,
end_delimiter=end_delimiter,
).strip()
return handler.format(post, **kwargs)


class Post(object):
Expand Down
8 changes: 8 additions & 0 deletions frontmatter/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import pytest


@pytest.fixture(autouse=True)
def add_globals(doctest_namespace):
import frontmatter

doctest_namespace["frontmatter"] = frontmatter
39 changes: 30 additions & 9 deletions frontmatter/default_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@

>>> import frontmatter
>>> from frontmatter.default_handlers import YAMLHandler, TOMLHandler
>>> post = frontmatter.load('tests/toml/hello-toml.markdown', handler=TOMLHandler())
>>> post = frontmatter.load('tests/toml/hello-toml.md', handler=TOMLHandler())
>>> post.handler #doctest: +ELLIPSIS
<frontmatter.default_handlers.TOMLHandler object at 0x...>

Expand Down Expand Up @@ -97,15 +97,10 @@

# set YAML format when dumping, but the old handler attached
>>> t1 = frontmatter.dumps(post, handler=YAMLHandler())

# set a new handler, changing all future exports
>>> post.handler = YAMLHandler()
>>> post.handler = YAMLHandler() # set a new handler, changing all future exports
>>> t2 = frontmatter.dumps(post)

# remove handler, defaulting back to YAML
>>> post.handler = None
>>> post.handler = None # remove handler, defaulting back to YAML
>>> t3 = frontmatter.dumps(post)

>>> t1 == t2 == t3
True

Expand All @@ -114,6 +109,7 @@
- split metadata and content, based on a boundary pattern (``handler.split``)
- parse plain text metadata into a Python dictionary (``handler.load``)
- export a dictionary back into plain text (``handler.export``)
- format exported metadata and content into a single string (``handler.format``)


"""
Expand Down Expand Up @@ -143,7 +139,16 @@
__all__.append("TOMLHandler")


class BaseHandler(object):
DEFAULT_POST_TEMPLATE = """\
{start_delimiter}
{metadata}
{end_delimiter}

{content}
"""


class BaseHandler:
"""
BaseHandler lays out all the steps to detecting, splitting, parsing and
exporting front matter metadata.
Expand Down Expand Up @@ -199,6 +204,22 @@ def export(self, metadata, **kwargs):
"""
raise NotImplementedError

def format(self, post, **kwargs):
"""
Turn a post into a string, used in ``frontmatter.dumps``
"""
start_delimiter = kwargs.pop("start_delimiter", self.START_DELIMITER)
end_delimiter = kwargs.pop("end_delimiter", self.END_DELIMITER)

metadata = self.export(post.metadata, **kwargs)

return DEFAULT_POST_TEMPLATE.format(
metadata=metadata,
content=post.content,
start_delimiter=start_delimiter,
end_delimiter=end_delimiter,
).strip()


class YAMLHandler(BaseHandler):
"""
Expand Down