Merge branch 'master' into peter
This commit is contained in:
commit
474bab8c4c
5 changed files with 118 additions and 23 deletions
12
README.adoc
12
README.adoc
|
|
@ -12,6 +12,18 @@ Requirements
|
|||
In Debian, installing the +python3-urwid+ package will generally satisfy the
|
||||
dependencies.
|
||||
|
||||
If you want to build the documentation as well, you'll need
|
||||
|
||||
* Asciidoctor
|
||||
* Apache FOP
|
||||
* DocBook XSL-NS stylesheets (the DocBook 5 ones)
|
||||
* xsltproc
|
||||
|
||||
Development requirements include
|
||||
|
||||
* pep8
|
||||
* pyflakes3
|
||||
|
||||
newfol is generally developed with the latest version of Python 3, but should be
|
||||
compatible with earlier versions. Please file an issue on GitHub if something
|
||||
is broken.
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ OBJ := $(SRC:.adoc=.pdf) $(SRC:.adoc=.html) $(SRC:.adoc=.ps)
|
|||
EXTRA_OBJ := $(SRC:.adoc=.xhtml)
|
||||
EXTRA_OBJ += $(SRC:.adoc=.xml) $(SRC:.adoc=.fo)
|
||||
ASCIIDOC := asciidoctor
|
||||
XSLTPROC := xsltproc
|
||||
FOP := fop
|
||||
XSLTPROC := xsltproc
|
||||
XSLT_PARAMS ?=
|
||||
DOCBOOK_XSL := /usr/share/xml/docbook/stylesheet/docbook-xsl-ns
|
||||
DOCBOOK_FO := $(DOCBOOK_XSL)/fo/docbook.xsl
|
||||
|
||||
|
|
@ -24,7 +25,7 @@ clean:
|
|||
$(ASCIIDOC) -b docbook5 -o $@ $<
|
||||
|
||||
%.fo: %.xml
|
||||
$(XSLTPROC) -o $@ $(DOCBOOK_FO) $<
|
||||
$(XSLTPROC) $(XSLT_PARAMS) -o $@ $(DOCBOOK_FO) $<
|
||||
|
||||
%.pdf: %.fo
|
||||
$(FOP) -fo $< -pdf $@
|
||||
|
|
|
|||
|
|
@ -142,3 +142,38 @@ settings, colors, and keybindings.
|
|||
|
||||
The default location is +_$XDG_CONFIG_HOME_/newfol/defaults+. If
|
||||
`$XDG_CONFIG_HOME` is not defined, it defaults to +_$HOME_/.config+.
|
||||
|
||||
== Key Bindings
|
||||
|
||||
Below are the default key bindings. These can be customized with the schema
|
||||
file and configuration file.
|
||||
|
||||
[options="header"]
|
||||
|===
|
||||
|Key | Shortcut Name | Meaning
|
||||
|F1 | previous | Go back one screen
|
||||
|F2 | sync-database | Save
|
||||
|F3 | add | Display the Add Record screen
|
||||
|F4 | search | Display the Search Records screen
|
||||
|F5 | next | Continue to the next screen
|
||||
|F6 | next-secondary | Continue; display results in editor read-only
|
||||
|F7 | menu | Display the menu
|
||||
|F9 | next-tertiary | Continue; display results in editor read-write
|
||||
|Ctrl-F5 | invert-selection | Toggle the current item
|
||||
|Ctrl-F | browse-all | Browse all records
|
||||
|Ctrl-B | browse | List the tables which have records to browse
|
||||
|Ctrl-X | about | Display the About screen
|
||||
|Alt-D | right | Go right
|
||||
|Alt-G | delete | Delete the character in front of the cursor
|
||||
|Alt-H | backspace | Delete the character behind the cursor
|
||||
|Alt-I | up | Go up
|
||||
|Alt-K | left | Go left
|
||||
|Alt-N | down | Go down
|
||||
|Alt-U | home | As if the Home key were pressed
|
||||
|Alt-Y | remove | Toggle whether the current record is deleted
|
||||
|Alt-; | end | As if the End key were pressed
|
||||
|Enter | select | Select a button
|
||||
|Tab | next-field | Go to the next field
|
||||
|Shift-Tab | previous-field | Go to the previous field
|
||||
|Shift-Q | quit | Quit the program
|
||||
|===
|
||||
|
|
|
|||
|
|
@ -546,11 +546,7 @@ class RawFile(FileFormat):
|
|||
class FileStorage:
|
||||
"""A file (or file-like object) in a certain format."""
|
||||
DEFAULT_TRANSACTION_TYPES = ('hash',)
|
||||
|
||||
def __init__(self, fmt, filename, txnformat=None, options=None):
|
||||
txnformat = self._canonicalize_transaction_types(txnformat)
|
||||
self._txn = self._make_transaction_store(txnformat, options)
|
||||
backends = {
|
||||
BACKENDS = {
|
||||
"csv": CSVFile,
|
||||
"qddb": QDDBFile,
|
||||
"pickle": PickleFile,
|
||||
|
|
@ -558,11 +554,44 @@ class FileStorage:
|
|||
"yaml": YAMLFile,
|
||||
"raw": RawFile
|
||||
}
|
||||
HOOKS = {
|
||||
"git": GitHook,
|
||||
"sha256": lambda *a, **k: HashHook("sha256", *a, **k),
|
||||
"sha384": lambda *a, **k: HashHook("sha384", *a, **k),
|
||||
"sha512": lambda *a, **k: HashHook("sha512", *a, **k),
|
||||
"hash": lambda *a, **k: HashHook(None, *a, **k),
|
||||
}
|
||||
|
||||
def __init__(self, fmt, filename, txnformat=None, options=None):
|
||||
txnformat = self._canonicalize_transaction_types(txnformat)
|
||||
self._txn = self._make_transaction_store(txnformat, options)
|
||||
try:
|
||||
self._backend = backends[fmt](filename, self._txn, options)
|
||||
self._backend = self.BACKENDS[fmt](filename, self._txn, options)
|
||||
except KeyError:
|
||||
raise ValueError("{0}: not a supported backend".format(fmt))
|
||||
|
||||
@classmethod
|
||||
def register_backend(klass, name, obj):
|
||||
klass.BACKENDS[name] = obj
|
||||
|
||||
@classmethod
|
||||
def unregister_backend(klass, name):
|
||||
try:
|
||||
del klass.BACKENDS[name]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def register_hook(klass, name, obj):
|
||||
klass.HOOKS[name] = obj
|
||||
|
||||
@classmethod
|
||||
def unregister_hook(klass, name):
|
||||
try:
|
||||
del klass.HOOKS[name]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _canonicalize_transaction_types(types):
|
||||
if types is None:
|
||||
|
|
@ -575,23 +604,13 @@ class FileStorage:
|
|||
stypes.discard("hash")
|
||||
return list(filter(lambda x: x in stypes, types))
|
||||
|
||||
@staticmethod
|
||||
def _get_transaction_types():
|
||||
return {
|
||||
"git": GitHook,
|
||||
"sha256": lambda *a, **k: HashHook("sha256", *a, **k),
|
||||
"sha384": lambda *a, **k: HashHook("sha384", *a, **k),
|
||||
"sha512": lambda *a, **k: HashHook("sha512", *a, **k),
|
||||
"hash": lambda *a, **k: HashHook(None, *a, **k),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def transaction_types():
|
||||
return list(sorted(FileStorage._get_transaction_types().keys()))
|
||||
return list(sorted(FileStorage.HOOKS.keys()))
|
||||
|
||||
@staticmethod
|
||||
def _make_transaction_store(items, options):
|
||||
txntypes = FileStorage._get_transaction_types()
|
||||
txntypes = FileStorage.HOOKS
|
||||
if items is None or items == "":
|
||||
return Hook()
|
||||
elif isinstance(items, str):
|
||||
|
|
|
|||
|
|
@ -241,5 +241,33 @@ class SHA256TransactionTest(unittest.TestCase):
|
|||
None)
|
||||
|
||||
|
||||
class ExampleFile(newfol.filemanip.FileFormat):
|
||||
def __init__(self, filename, txn, options):
|
||||
self.inited = True
|
||||
|
||||
def store(self, records):
|
||||
self.stored = True
|
||||
pass
|
||||
|
||||
def load(self):
|
||||
return [Record(['a', 'b'])]
|
||||
|
||||
|
||||
class PluggableBackendsTest(unittest.TestCase):
|
||||
def test_adding_backend(self):
|
||||
FileStorage.register_backend('example', ExampleFile)
|
||||
self.assertEqual(FileStorage.BACKENDS['example'], ExampleFile)
|
||||
fs = FileStorage('example', 'filename')
|
||||
recs = fs.load()
|
||||
self.assertEqual(len(recs), 1)
|
||||
self.assertEqual(recs[0].fields, ['a', 'b'])
|
||||
|
||||
def test_removing_backend(self):
|
||||
FileStorage.register_backend('example', ExampleFile)
|
||||
self.assertEqual(FileStorage.BACKENDS['example'], ExampleFile)
|
||||
FileStorage.unregister_backend('example')
|
||||
with self.assertRaises(KeyError):
|
||||
FileStorage.BACKENDS['example']
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
|||
Loading…
Reference in a new issue