forked from t4ngo/dragonfly
-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request t4ngo#49 from Danesprite/feat/text_engine
Closes t4ngo#36.
- Loading branch information
Showing
22 changed files
with
1,052 additions
and
201 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
|
||
.. _RefCLI: | ||
|
||
Command-line Interface (CLI) | ||
============================================================================ | ||
|
||
.. argparse:: | ||
:module: dragonfly.__main__ | ||
:func: make_arg_parser | ||
|
||
|
||
Usage Examples | ||
---------------------------------------------------------------------------- | ||
Below are some examples using the *test* sub-command. All of them should | ||
work in Bash. | ||
|
||
.. code:: shell | ||
|
||
# Load a command module and mimic two commands separately. | ||
echo "command one\n command two" | python -m dragonfly test module.py | ||
|
||
# Same test without the pipe. | ||
python -m dragonfly test module.py | ||
command one | ||
command two | ||
|
||
# Same test with quieter output. | ||
echo "command one\n command two" | python -m dragonfly test -q module.py | ||
|
||
# Test loading two modules with the sphinx engine and exit without | ||
# checking input. | ||
python -m dragonfly test -e sphinx --no-input module1.py module2.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ Contents: | |
.. toctree:: | ||
config | ||
ccr | ||
cli | ||
language | ||
windows | ||
accessibility |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,4 @@ pyperclip>=1.7.0 | |
six | ||
enum34;python_version<'3.4' | ||
regex | ||
sphinx-argparse |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import argparse | ||
import logging | ||
import sys | ||
|
||
|
||
from dragonfly import get_engine, MimicFailure | ||
from dragonfly.engines.base.engine import EngineContext | ||
from dragonfly.loader import CommandModule | ||
|
||
logging.basicConfig() | ||
_log = logging.getLogger("command") | ||
|
||
|
||
def test_with_engine(args): | ||
# Initialise the specified engine, catching and reporting errors. | ||
try: | ||
engine = get_engine(args.engine) | ||
_log.debug("Testing with engine '%s'" % args.engine) | ||
except Exception as e: | ||
_log.error(e) | ||
return 1 | ||
|
||
# Set the logging level of the root logger. | ||
if args.quiet: | ||
args.log_level = "WARNING" | ||
logging.root.setLevel(getattr(logging, args.log_level)) | ||
|
||
# Connect to the engine, load grammar modules, take input from stdin and | ||
# disconnect from the engine if interrupted or if EOF is received. | ||
with EngineContext(engine): | ||
# Load each module. Errors during loading will be caught and logged. | ||
failed_loads = 0 | ||
for f in args.files: | ||
module_ = CommandModule(f.name) | ||
module_.load() | ||
|
||
if not module_.loaded: | ||
failed_loads += 1 | ||
|
||
# Also close each file object created by argparse. | ||
f.close() | ||
|
||
# Read lines from stdin and pass them to engine.mimic. Strip excess | ||
# white space from each line. Report any mimic failures. | ||
# Use the success of the last call to engine.mimic as the return | ||
# code. If there were no non-empty lines from stdin, the overall | ||
# success of module loading will be used instead. | ||
return_code = 1 if failed_loads else 0 | ||
if args.no_input: | ||
# Return early if --no-input was specified. | ||
return return_code | ||
_log.info("Enter commands to mimic followed by new lines.") | ||
try: | ||
# Use iter to avoid a bug in Python 2.x: | ||
# https://bugs.python.org/issue3907 | ||
for line in iter(sys.stdin.readline, ''): | ||
line = line.strip() | ||
if not line: # skip empty lines. | ||
continue | ||
|
||
try: | ||
engine.mimic(line.split()) | ||
_log.info("Mimic success for words: %s" % line) | ||
return_code = 0 | ||
except MimicFailure: | ||
_log.error("Mimic failure for words: %s" % line) | ||
return_code = 1 | ||
except KeyboardInterrupt: | ||
pass | ||
|
||
return return_code | ||
|
||
|
||
_command_map = { | ||
"test": test_with_engine | ||
} | ||
|
||
|
||
def make_arg_parser(): | ||
parser = argparse.ArgumentParser( | ||
prog="python -m dragonfly", | ||
description="Command-line interface to the Dragonfly speech " | ||
"recognition framework" | ||
) | ||
subparsers = parser.add_subparsers(dest='subparser_name') | ||
|
||
# Create the parser for the "test" command. | ||
parser_test = subparsers.add_parser( | ||
"test", | ||
help="Load grammars from Python files for testing with a " | ||
"dragonfly engine. By default input from stdin is passed to " | ||
"engine.mimic() after command modules are loaded." | ||
) | ||
parser_test.add_argument( | ||
"files", metavar="file", nargs="+", type=argparse.FileType("r"), | ||
help="Command module file(s)." | ||
) | ||
parser_test.add_argument( | ||
"-e", "--engine", default="text", | ||
help="Name of the engine to use for testing." | ||
) | ||
parser_test.add_argument( | ||
"-l", "--log-level", default="INFO", | ||
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], | ||
help="Log level to use." | ||
) | ||
parser_test.add_argument( | ||
"-n", "--no-input", default=False, action="store_true", | ||
help="Whether to load command modules and then exit without " | ||
"reading input from stdin." | ||
) | ||
parser_test.add_argument( | ||
"-q", "--quiet", default=False, action="store_true", | ||
help="Equivalent to '-l WARNING' -- suppresses INFO and DEBUG " | ||
"logging." | ||
) | ||
return parser | ||
|
||
|
||
def main(): | ||
# Parse the arguments and get the relevant function. Exit if the command | ||
# is not implemented. | ||
args = make_arg_parser().parse_args() | ||
|
||
def not_implemented(_): | ||
print("Command '%s' is not implemented" % args.subparser_name) | ||
return 1 | ||
|
||
func = _command_map.get(args.subparser_name, not_implemented) | ||
|
||
# Call the function and exit using the result. | ||
return_code = func(args) | ||
exit(return_code) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# | ||
# This file is part of Dragonfly. | ||
# (c) Copyright 2018 by Dane Finlay | ||
# Licensed under the LGPL. | ||
# | ||
# Dragonfly is free software: you can redistribute it and/or modify it | ||
# under the terms of the GNU Lesser General Public License as published | ||
# by the Free Software Foundation, either version 3 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# Dragonfly is distributed in the hope that it will be useful, but | ||
# WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
# Lesser General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Lesser General Public | ||
# License along with Dragonfly. If not, see | ||
# <http://www.gnu.org/licenses/>. | ||
# | ||
|
||
""" | ||
SR back-end package for the text input engine | ||
============================================================================ | ||
The text input engine is a convenient, always available implementation | ||
designed to be used via the :meth:`engine.mimic` method. | ||
To initialise the text input engine, do the following:: | ||
get_engine("text") | ||
Note that :meth:`dragonfly.engines.get_engine` called without ``"text"`` | ||
will **never** initialise the text input engine. This is because real speech | ||
recognition backends should be returned from the function by default. | ||
All dragonfly elements and rule classes should be supported. Use all | ||
uppercase words to mimic input for :class:`Dictation` elements, e.g. | ||
`"find SOME TEXT"` to match the dragonfly spec `"find <text>"`. | ||
Dragonfly's command-line interface can be used to test command modules with | ||
the text input engine. See the :ref:`CLI page <RefCLI>` for more details. | ||
""" | ||
|
||
import logging | ||
_log = logging.getLogger("engine.text") | ||
|
||
|
||
# Module level singleton instance of this engine implementation. | ||
_engine = None | ||
|
||
|
||
def is_engine_available(): | ||
""" Check whether the engine is available. """ | ||
return True | ||
|
||
|
||
def get_engine(): | ||
""" Retrieve the back-end engine object. """ | ||
global _engine | ||
if not _engine: | ||
from .engine import TextInputEngine | ||
_engine = TextInputEngine() | ||
return _engine |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# | ||
# This file is part of Dragonfly. | ||
# (c) Copyright 2018 by Dane Finlay | ||
# Licensed under the LGPL. | ||
# | ||
# Dragonfly is free software: you can redistribute it and/or modify it | ||
# under the terms of the GNU Lesser General Public License as published | ||
# by the Free Software Foundation, either version 3 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# Dragonfly is distributed in the hope that it will be useful, but | ||
# WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
# Lesser General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Lesser General Public | ||
# License along with Dragonfly. If not, see | ||
# <http://www.gnu.org/licenses/>. | ||
# | ||
|
||
""" | ||
Dictation container class for the text engine | ||
============================================================================ | ||
This class is used to store the recognized results of dictation elements | ||
within voice-commands. It offers access to both the raw spoken-form words | ||
and be formatted written-form text. | ||
The formatted text can be retrieved using | ||
:meth:`~DictationContainerBase.format` or simply by calling ``str(...)`` | ||
on a dictation container object. A tuple of the raw spoken words can be | ||
retrieved using :attr:`~DictationContainerBase.words`. | ||
""" | ||
|
||
from six import PY2 | ||
|
||
from ..base import DictationContainerBase | ||
|
||
|
||
class TextDictationContainer(DictationContainerBase): | ||
""" | ||
Container class for dictated words as recognized by the | ||
:class:`Dictation` element. | ||
""" | ||
|
||
def __init__(self, words): | ||
DictationContainerBase.__init__(self, words=words) | ||
|
||
def __repr__(self): | ||
message = u"%s(%s)" % (self.__class__.__name__, | ||
u", ".join(self._words)) | ||
if PY2: | ||
return message.encode() | ||
else: | ||
return message |
Oops, something went wrong.