mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-07-02 06:56:12 +02:00
Use implicit namespace plugins for import and export (#1216)
* behavior outline
* FIrst pass at allow external plugins
* remove template exporter
* Add listing of active plugins to '--version' output
* Documentation for plugins
* [Docs] add custom imports and exporters to site TOC
* [Docs] better linewrapping
* enforce positive initial linewrap
Check column widths
update gitignore
throw error when linewrap too small
simply check for large enough linewrap value
* delete unused error message
* PR feedback
make exception more informative
update check_linewrap signature in src and test
make check_linewrap a free function
* delete unused function
* delete else..pass block
* newline for make format
* Include dates_exporter
* Use Base classes for importer and exporters.
* [Docs] improve documentation of custom Importers and Exporters
* [Testing] separate run with external plugin!
* basic behavior test
* prototype unittest for JSON Exporter
test for unimplemented method
* make format
delete unused imports
* Remove 'importer' or 'exporter' from filenames where not needed
* [Test] run different tests with or without the external plugins installed
* [Test] move test rot13 plugin into git tree
from 0dc912af82
* consolidate demo plugins to common package
* [Docs] name page for plugins
* [Docs] include the sample plug in code files directly
* style fixes
* [test] determine whether to run external plug in tests based on installed packages
* improved code documentation
* style fixes for GitHub actions
* Convert "short" and "pretty" (and "default") formaters to plugins
further to https://github.com/jrnl-org/jrnl/pull/1177
* more code clean up
tests pass locally...now for GitHub...
* [tests] dynamically determine jrnl version for plugin tests
* [GitHub Actions] direct install of testing plugins
* Remove template code
* [plugins] meta --> collector
* [Docs] create scripted entries using an custom importer
* (closer to) being able to run behave tests outside project root directory
* We already know when exporter to use
Don't re-calculate it!
* [Tests] don't name test plugin 'testing"
If so named, pip won't install it.
* [Test] run behave tests with test plugins outside project root
* [Test] behave tests pass locally
* [Docs] fix typo
* [GitHub Actions] run test commands from poetry's shell
* black-ify code
* [GitHub Actions] move downstream (rather than up) to run tests
* [GitHub Actions] set shell to poetry
* [GitHub Workflows] Manually activate virtual environment
* [GitHub Actions] Skip Windows & Python 3.8
Can't seem to find Python exe?
* [GiotHub Actions] explicitly use virtual env
* [GitHub Actions] create virutal env directly
* [GitHub Actions] better activate of Windows virtual env
* [GitHub Actions] create virtual env on Mac
* [Github Actions] install wheel and upgrade pip
* [GitHub Actions] skip virtual environments altogether
* [GitHub Actions] change directory for behave test
* Remove Windows exclusions from CI as per note -- they should be working now
Co-authored-by: Suhas <sugas182@gmail.com>
Co-authored-by: Micah Jerome Ellison <micah.jerome.ellison@gmail.com>
This commit is contained in:
parent
cef3a98b4e
commit
4392e29742
45 changed files with 1021 additions and 383 deletions
9
tests/external_plugins_src/README.md
Normal file
9
tests/external_plugins_src/README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Rot13 Custom Exporter for Jrnl
|
||||
|
||||
This is a custom exporter to demostrate how to write customer exporters for
|
||||
[jrnl](https://github.com/jrnl-org/jrnl). It is also used by *jrnl* in its
|
||||
tests to ensure the feature works as expected.
|
||||
|
||||
This plugin applies a [Caeser
|
||||
cipher](https://en.wikipedia.org/wiki/Caesar_cipher) (specifically the
|
||||
[ROT13](https://en.wikipedia.org/wiki/ROT13)) to output text.
|
|
@ -0,0 +1,37 @@
|
|||
# pelican\contrib\exporter\custom_json.py
|
||||
import json
|
||||
|
||||
from jrnl.plugins.base import BaseExporter
|
||||
|
||||
__version__ = "v1.0.0"
|
||||
|
||||
|
||||
class Exporter(BaseExporter):
|
||||
"""
|
||||
This basic Exporter can convert entries and journals into JSON.
|
||||
"""
|
||||
|
||||
names = ["json"]
|
||||
extension = "json"
|
||||
version = __version__
|
||||
|
||||
@classmethod
|
||||
def entry_to_dict(cls, entry):
|
||||
return {
|
||||
"title": entry.title,
|
||||
"body": entry.body,
|
||||
"date": entry.date.strftime("%Y-%m-%d"),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def export_entry(cls, entry):
|
||||
"""Returns a json representation of a single entry."""
|
||||
return json.dumps(cls.entry_to_dict(entry), indent=2) + "\n"
|
||||
|
||||
@classmethod
|
||||
def export_journal(cls, journal):
|
||||
"""Returns a json representation of an entire journal."""
|
||||
result = {
|
||||
"entries": [cls.entry_to_dict(e) for e in journal.entries],
|
||||
}
|
||||
return json.dumps(result, indent=2)
|
25
tests/external_plugins_src/jrnl/contrib/exporter/flag.py
Normal file
25
tests/external_plugins_src/jrnl/contrib/exporter/flag.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
# Copyright (C) 2012-2021 jrnl contributors
|
||||
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
"""
|
||||
Exporter for testing and experimentation purposes.
|
||||
|
||||
It is not called "testing" because then it's not installed.
|
||||
|
||||
The presence of this plugin is also used as a "switch" by the test suite to
|
||||
decide on whether or not to run the "vanilla" test suite, or the test suite
|
||||
for external plugins.
|
||||
|
||||
The `export_entry` and `export_journal` methods are both purposely not
|
||||
implemented to confirm behavior on plugins that don't implement them.
|
||||
"""
|
||||
|
||||
from jrnl.plugins.base import BaseExporter
|
||||
|
||||
|
||||
class Exporter(BaseExporter):
|
||||
names = ["testing", "test"]
|
||||
version = "v0.0.1"
|
||||
extension = "test"
|
15
tests/external_plugins_src/jrnl/contrib/exporter/rot13.py
Normal file
15
tests/external_plugins_src/jrnl/contrib/exporter/rot13.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
import codecs
|
||||
|
||||
from jrnl.plugins.base import BaseExporter
|
||||
|
||||
__version__ = "v1.0.0"
|
||||
|
||||
|
||||
class Exporter(BaseExporter):
|
||||
names = ["rot13", "txt"]
|
||||
extension = "txt"
|
||||
version = __version__
|
||||
|
||||
@classmethod
|
||||
def export_entry(cls, entry):
|
||||
return codecs.encode(str(entry), "rot_13")
|
|
@ -0,0 +1,50 @@
|
|||
# pelican\contrib\importer\sample_json.py
|
||||
import json
|
||||
import sys
|
||||
|
||||
from jrnl import Entry
|
||||
from jrnl.plugins.base import BaseImporter
|
||||
|
||||
__version__ = "v1.0.0"
|
||||
|
||||
|
||||
class Importer(BaseImporter):
|
||||
"""JSON Importer for jrnl."""
|
||||
|
||||
names = ["json"]
|
||||
version = __version__
|
||||
|
||||
@staticmethod
|
||||
def import_(journal, input=None):
|
||||
"""
|
||||
Given a nicely formatted JSON file, will add the
|
||||
contained Entries to the journal.
|
||||
"""
|
||||
|
||||
old_cnt = len(journal.entries)
|
||||
if input:
|
||||
with open(input, "r", encoding="utf-8") as f:
|
||||
data = json.loads(f)
|
||||
else:
|
||||
try:
|
||||
data = sys.stdin.read()
|
||||
except KeyboardInterrupt:
|
||||
print(
|
||||
"[Entries NOT imported into journal.]",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(0)
|
||||
|
||||
for json_entry in data:
|
||||
raw = json_entry["title"] + "/n" + json_entry["body"]
|
||||
date = json_entry["date"]
|
||||
entry = Entry.Entry(journal, date, raw)
|
||||
journal.entries.append(entry)
|
||||
|
||||
new_cnt = len(journal.entries)
|
||||
print(
|
||||
"[{} entries imported to '{}' journal]".format(
|
||||
new_cnt - old_cnt, journal.name
|
||||
),
|
||||
file=sys.stderr,
|
||||
)
|
35
tests/external_plugins_src/setup.py
Normal file
35
tests/external_plugins_src/setup.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
import os
|
||||
import re
|
||||
|
||||
try:
|
||||
from setuptools import setup
|
||||
except ImportError:
|
||||
from distutils.core import setup
|
||||
|
||||
base_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def get_version(filename="jrnl/contrib/exporter/rot13.py"):
|
||||
with open(os.path.join(base_dir, filename), encoding="utf-8") as initfile:
|
||||
for line in initfile.readlines():
|
||||
m = re.match("__version__ *= *['\"](.*)['\"]", line)
|
||||
if m:
|
||||
return m.group(1)
|
||||
|
||||
|
||||
setup(
|
||||
name="jrnl-demo-plugins",
|
||||
version=get_version(),
|
||||
description="Demonstration custom plugins for jrnl",
|
||||
long_description="\n\n".join([open(os.path.join(base_dir, "README.md")).read()]),
|
||||
long_description_content_type="text/markdown",
|
||||
author="W. Minchin",
|
||||
author_email="w_minchin@hotmail.com",
|
||||
url="https://github.com/jrnl-org/jrnl/tree/develop/tests/external_plugins_src",
|
||||
packages=["jrnl", "jrnl.contrib", "jrnl.contrib.exporter", "jrnl.contrib.importer"],
|
||||
include_package_data=True,
|
||||
install_requires=[
|
||||
"jrnl",
|
||||
],
|
||||
zip_safe=False, # use wheels instead
|
||||
)
|
46
tests/test_plugin.py
Normal file
46
tests/test_plugin.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
from datetime import date
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from jrnl import Entry
|
||||
from jrnl import Journal
|
||||
from jrnl.plugins.exporter import json as json_exporter
|
||||
|
||||
try:
|
||||
from jrnl.contrib.exporter import testing as testing_exporter
|
||||
except:
|
||||
testing_exporter = None
|
||||
|
||||
|
||||
if testing_exporter:
|
||||
|
||||
@pytest.fixture()
|
||||
def create_entry():
|
||||
entry = Entry.Entry(
|
||||
journal=Journal.Journal(),
|
||||
text="This is the entry text",
|
||||
date=date(year=2001, month=1, day=1),
|
||||
starred=True,
|
||||
)
|
||||
yield entry
|
||||
|
||||
class TestBaseExporter(testing_exporter.Exporter):
|
||||
def test_unimplemented_export(self, create_entry):
|
||||
entry = create_entry
|
||||
with pytest.raises(NotImplementedError):
|
||||
self.export_entry(entry)
|
||||
|
||||
class TestJsonExporter(json_exporter.Exporter):
|
||||
def test_json_exporter_name(self):
|
||||
assert "json" in self.names
|
||||
|
||||
def test_export_entry(self, create_entry):
|
||||
entry = create_entry
|
||||
fake_uuid = "ewqf09-432p9p0433-243209" # generated by mashing keys
|
||||
entry.uuid = fake_uuid
|
||||
exported = self.export_entry(entry)
|
||||
deserialized_export = json.loads(exported)
|
||||
assert deserialized_export["title"] == "This is the entry text"
|
||||
assert deserialized_export["date"] == "2001-01-01"
|
||||
assert "uuid" in deserialized_export.keys()
|
|
@ -9,7 +9,7 @@ from jrnl.jrnl import _display_search_results
|
|||
|
||||
# fmt: off
|
||||
# see: https://github.com/psf/black/issues/664
|
||||
@pytest.mark.parametrize("export_format", [ "pretty", "short","markdown"])
|
||||
@pytest.mark.parametrize("export_format", [ "pretty", "short", "markdown"])
|
||||
#fmt: on
|
||||
@mock.patch.object(argparse, "Namespace", return_value={"export": "markdown", "filename": "irrele.vant"})
|
||||
def test_export_format(mock_args, export_format):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue