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
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.5, 3.6, 3.7, 3.8, 3.9]
python-version: [3.6, 3.7, 3.8, 3.9]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
Expand All @@ -27,7 +27,7 @@ jobs:
run: |
pip install -e '.[test]'
- name: Run tests
run: python test.py
run: pytest
deploy:
runs-on: ubuntu-latest
needs: [test]
Expand Down
5 changes: 2 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.5, 3.6, 3.7, 3.8, 3.9]
python-version: [3.6, 3.7, 3.8, 3.9]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
Expand All @@ -25,5 +25,4 @@ jobs:
run: |
pip install -e '.[test]'
- name: Run tests
run: |
python test.py
run: pytest
7 changes: 2 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
language: python
dist: xenial
python:
- "3.5"
- "3.6"
- "3.7"
- "3.8"
- "3.9"
# command to install dependencies
install:
# use pyaml for tests above py26
- pip install -r requirements.txt
- "python setup.py install"
- "pip install -e .[test]"
# command to run tests
script: python test.py
script: pytest
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2019 Chris Amico, Glass Eye Media LLC
Copyright (c) 2021 Chris Amico

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,21 @@ import frontmatter
Load a post from a filename:

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

Or a file (or file-like object):

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

```

Or load from text:

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

```
Expand Down Expand Up @@ -74,7 +74,7 @@ Metadata is a dictionary, with some handy proxies:
If you don't need the whole post object, just parse:

```python
>>> with open('tests/hello-world.markdown') 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 @@ -109,3 +109,5 @@ title: Hello, world!
Well, hello there, world.

```

For more examples, see files in the `tests/` directory. Each sample file has a corresponding `.result.json` file showing the expected parsed output.
6 changes: 3 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,17 @@

# General information about the project.
project = u"Python Frontmatter"
copyright = u"2017, Chris Amico"
copyright = u"2021, Chris Amico"
author = u"Chris Amico"

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = u"0.4.2"
version = u"0.5.0"
# The full version, including alpha/beta/rc tags.
release = u"0.4.2"
release = u"0.5.0"

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
12 changes: 6 additions & 6 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Python Frontmatter
useful way to add arbitrary, structured metadata to text documents,
regardless of type.

This is a small package to load and parse files (or just text) with YAML
This is a package to load and parse files (or text strings) with YAML
front matter.


Expand All @@ -34,20 +34,20 @@ Load a post from a filename:

::

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

Or a file (or file-like object):

::

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

Or load from text:

::

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

Access content:
Expand Down Expand Up @@ -80,11 +80,11 @@ Metadata is a dictionary, with some handy proxies:
>>> pprint(post.metadata)
{'excerpt': 'tl;dr', 'layout': 'post', 'title': 'Hello, world!'}

If you don't need the whole post object, just parse:
If you don't need the whole post object, use `frontmatter.parse` to return metadata and content separately:

::

>>> with open('tests/hello-world.markdown') as f:
>>> with open('tests/yaml/hello-world.txt') as f:
... metadata, content = frontmatter.parse(f.read())
>>> print(metadata['title'])
Hello, world!
Expand Down
68 changes: 53 additions & 15 deletions frontmatter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,13 @@ def parse(text, encoding="utf-8", handler=None, **defaults):
If frontmatter is not found, returns an empty metadata dictionary
(or defaults) and original text content.

::
.. testsetup:: *

import frontmatter

>>> with open('tests/hello-world.markdown') as f:
.. doctest::

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

If it contains a frontmatter but it is empty, return True as well.

::
.. doctest::

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

"""
Expand All @@ -119,9 +123,9 @@ def checks(text, encoding="utf-8"):

If it contains a frontmatter but it is empty, return True as well.

::
.. doctest::

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

Expand All @@ -135,10 +139,10 @@ def load(fd, encoding="utf-8", handler=None, **defaults):
Load and parse a file-like object or filename,
return a :py:class:`post <frontmatter.Post>`.

::
.. doctest::

>>> post = frontmatter.load('tests/hello-world.markdown')
>>> with open('tests/hello-world.markdown') 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 @@ -157,9 +161,9 @@ def loads(text, encoding="utf-8", handler=None, **defaults):
"""
Parse text (binary or unicode) and return a :py:class:`post <frontmatter.Post>`.

::
.. doctest::

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

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

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

Well, hello there, world.


.. testcode::

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

.. testoutput::

---
layout: post
title: Hello, world!
---

Well, hello there, world.

"""
content = dumps(post, handler, **kwargs)
if hasattr(fd, "write"):
Expand All @@ -207,14 +229,30 @@ def dumps(post, handler=None, **kwargs):
passed as an argument will override ``post.handler``, with
:py:class:`YAMLHandler <frontmatter.default_handlers.YAMLHandler>` used as
a default.

::

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

Well, hello there, world.

.. testcode::

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

.. testoutput::

---
layout: post
title: Hello, world!
---

Well, hello there, world.

"""
Expand Down
7 changes: 6 additions & 1 deletion frontmatter/default_handlers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# -*- coding: utf-8 -*-
"""
.. testsetup:: handlers

import frontmatter

By default, ``frontmatter`` reads and writes YAML metadata. But maybe
you don't like YAML. Maybe enjoy writing metadata in JSON, or TOML, or
some other exotic markup not yet invented. For this, there are handlers.
Expand Down Expand Up @@ -31,11 +35,12 @@
object. By default, calling :py:func:`frontmatter.dumps <frontmatter.dumps>`
on the post will use the attached handler.


::

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

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
packages=["frontmatter"],
include_package_data=True,
install_requires=["PyYAML"],
extras_require={"test": ["pytest", "toml"]},
extras_require={"test": ["pytest", "toml", "pyaml"], "docs": ["sphinx"]},
tests_require=["python-frontmatter[test]"],
license="MIT",
zip_safe=False,
Expand Down
3 changes: 3 additions & 0 deletions tests/empty/empty-frontmatter.result.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"content": "I have frontmatter but no metadata."
}
File renamed without changes.
3 changes: 3 additions & 0 deletions tests/empty/no-frontmatter.result.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"content": "I have no frontmatter."
}
File renamed without changes.
File renamed without changes.
6 changes: 6 additions & 0 deletions tests/json/hello-json.result.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"test": "tester",
"author": "bob",
"something": "else",
"content": "Title\n=====\n\ntitle2\n------\n\nHello.\n\nJust need three dashes\n---\n\nAnd this might break."
}
21 changes: 21 additions & 0 deletions tests/stub_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env python
"""
Generate result files for test content. Won't overwrite any that exit.
"""
import json
from pathlib import Path

import frontmatter
from test_files import files, get_result_filename


def main():
for path in files():
result = Path(get_result_filename(path))
if not result.exists():
post = frontmatter.loads(path.read_text())
result.write_text(json.dumps(post.to_dict(), indent=2), "utf-8")


if __name__ == "__main__":
main()
17 changes: 17 additions & 0 deletions tests/test_docs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# all the doctests here
import doctest
import frontmatter


def test_readme():
doctest.testfile("../README.md", extraglobs={"frontmatter": frontmatter})


def test_api_docs():
doctest.testmod(frontmatter, extraglobs={"frontmatter": frontmatter})


def test_handler_docs():
doctest.testmod(
frontmatter.default_handlers, extraglobs={"frontmatter": frontmatter}
)
Loading