about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are here HEAD master
Diffstat (limited to '.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand')
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/__init__.py27
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autoasync.py142
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autocommand.py70
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/automain.py59
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autoparse.py333
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/errors.py23
6 files changed, 654 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/__init__.py
new file mode 100644
index 00000000..73fbfca6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/__init__.py
@@ -0,0 +1,27 @@
+# Copyright 2014-2016 Nathan West
+#
+# This file is part of autocommand.
+#
+# autocommand 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.
+#
+# autocommand 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 autocommand.  If not, see <http://www.gnu.org/licenses/>.
+
+# flake8 flags all these imports as unused, hence the NOQAs everywhere.
+
+from .automain import automain  # NOQA
+from .autoparse import autoparse, smart_open  # NOQA
+from .autocommand import autocommand  # NOQA
+
+try:
+    from .autoasync import autoasync  # NOQA
+except ImportError:  # pragma: no cover
+    pass
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autoasync.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autoasync.py
new file mode 100644
index 00000000..688f7e05
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autoasync.py
@@ -0,0 +1,142 @@
+# Copyright 2014-2015 Nathan West
+#
+# This file is part of autocommand.
+#
+# autocommand 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.
+#
+# autocommand 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 autocommand.  If not, see <http://www.gnu.org/licenses/>.
+
+from asyncio import get_event_loop, iscoroutine
+from functools import wraps
+from inspect import signature
+
+
+async def _run_forever_coro(coro, args, kwargs, loop):
+    '''
+    This helper function launches an async main function that was tagged with
+    forever=True. There are two possibilities:
+
+    - The function is a normal function, which handles initializing the event
+      loop, which is then run forever
+    - The function is a coroutine, which needs to be scheduled in the event
+      loop, which is then run forever
+      - There is also the possibility that the function is a normal function
+        wrapping a coroutine function
+
+    The function is therefore called unconditionally and scheduled in the event
+    loop if the return value is a coroutine object.
+
+    The reason this is a separate function is to make absolutely sure that all
+    the objects created are garbage collected after all is said and done; we
+    do this to ensure that any exceptions raised in the tasks are collected
+    ASAP.
+    '''
+
+    # Personal note: I consider this an antipattern, as it relies on the use of
+    # unowned resources. The setup function dumps some stuff into the event
+    # loop where it just whirls in the ether without a well defined owner or
+    # lifetime. For this reason, there's a good chance I'll remove the
+    # forever=True feature from autoasync at some point in the future.
+    thing = coro(*args, **kwargs)
+    if iscoroutine(thing):
+        await thing
+
+
+def autoasync(coro=None, *, loop=None, forever=False, pass_loop=False):
+    '''
+    Convert an asyncio coroutine into a function which, when called, is
+    evaluted in an event loop, and the return value returned. This is intented
+    to make it easy to write entry points into asyncio coroutines, which
+    otherwise need to be explictly evaluted with an event loop's
+    run_until_complete.
+
+    If `loop` is given, it is used as the event loop to run the coro in. If it
+    is None (the default), the loop is retreived using asyncio.get_event_loop.
+    This call is defered until the decorated function is called, so that
+    callers can install custom event loops or event loop policies after
+    @autoasync is applied.
+
+    If `forever` is True, the loop is run forever after the decorated coroutine
+    is finished. Use this for servers created with asyncio.start_server and the
+    like.
+
+    If `pass_loop` is True, the event loop object is passed into the coroutine
+    as the `loop` kwarg when the wrapper function is called. In this case, the
+    wrapper function's __signature__ is updated to remove this parameter, so
+    that autoparse can still be used on it without generating a parameter for
+    `loop`.
+
+    This coroutine can be called with ( @autoasync(...) ) or without
+    ( @autoasync ) arguments.
+
+    Examples:
+
+    @autoasync
+    def get_file(host, port):
+        reader, writer = yield from asyncio.open_connection(host, port)
+        data = reader.read()
+        sys.stdout.write(data.decode())
+
+    get_file(host, port)
+
+    @autoasync(forever=True, pass_loop=True)
+    def server(host, port, loop):
+        yield_from loop.create_server(Proto, host, port)
+
+    server('localhost', 8899)
+
+    '''
+    if coro is None:
+        return lambda c: autoasync(
+            c, loop=loop,
+            forever=forever,
+            pass_loop=pass_loop)
+
+    # The old and new signatures are required to correctly bind the loop
+    # parameter in 100% of cases, even if it's a positional parameter.
+    # NOTE: A future release will probably require the loop parameter to be
+    # a kwonly parameter.
+    if pass_loop:
+        old_sig = signature(coro)
+        new_sig = old_sig.replace(parameters=(
+            param for name, param in old_sig.parameters.items()
+            if name != "loop"))
+
+    @wraps(coro)
+    def autoasync_wrapper(*args, **kwargs):
+        # Defer the call to get_event_loop so that, if a custom policy is
+        # installed after the autoasync decorator, it is respected at call time
+        local_loop = get_event_loop() if loop is None else loop
+
+        # Inject the 'loop' argument. We have to use this signature binding to
+        # ensure it's injected in the correct place (positional, keyword, etc)
+        if pass_loop:
+            bound_args = old_sig.bind_partial()
+            bound_args.arguments.update(
+                loop=local_loop,
+                **new_sig.bind(*args, **kwargs).arguments)
+            args, kwargs = bound_args.args, bound_args.kwargs
+
+        if forever:
+            local_loop.create_task(_run_forever_coro(
+                coro, args, kwargs, local_loop
+            ))
+            local_loop.run_forever()
+        else:
+            return local_loop.run_until_complete(coro(*args, **kwargs))
+
+    # Attach the updated signature. This allows 'pass_loop' to be used with
+    # autoparse
+    if pass_loop:
+        autoasync_wrapper.__signature__ = new_sig
+
+    return autoasync_wrapper
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autocommand.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autocommand.py
new file mode 100644
index 00000000..097e86de
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autocommand.py
@@ -0,0 +1,70 @@
+# Copyright 2014-2015 Nathan West
+#
+# This file is part of autocommand.
+#
+# autocommand 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.
+#
+# autocommand 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 autocommand.  If not, see <http://www.gnu.org/licenses/>.
+
+from .autoparse import autoparse
+from .automain import automain
+try:
+    from .autoasync import autoasync
+except ImportError:  # pragma: no cover
+    pass
+
+
+def autocommand(
+        module, *,
+        description=None,
+        epilog=None,
+        add_nos=False,
+        parser=None,
+        loop=None,
+        forever=False,
+        pass_loop=False):
+
+    if callable(module):
+        raise TypeError('autocommand requires a module name argument')
+
+    def autocommand_decorator(func):
+        # Step 1: if requested, run it all in an asyncio event loop. autoasync
+        # patches the __signature__ of the decorated function, so that in the
+        # event that pass_loop is True, the `loop` parameter of the original
+        # function will *not* be interpreted as a command-line argument by
+        # autoparse
+        if loop is not None or forever or pass_loop:
+            func = autoasync(
+                func,
+                loop=None if loop is True else loop,
+                pass_loop=pass_loop,
+                forever=forever)
+
+        # Step 2: create parser. We do this second so that the arguments are
+        # parsed and passed *before* entering the asyncio event loop, if it
+        # exists. This simplifies the stack trace and ensures errors are
+        # reported earlier. It also ensures that errors raised during parsing &
+        # passing are still raised if `forever` is True.
+        func = autoparse(
+            func,
+            description=description,
+            epilog=epilog,
+            add_nos=add_nos,
+            parser=parser)
+
+        # Step 3: call the function automatically if __name__ == '__main__' (or
+        # if True was provided)
+        func = automain(module)(func)
+
+        return func
+
+    return autocommand_decorator
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/automain.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/automain.py
new file mode 100644
index 00000000..6cc45db6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/automain.py
@@ -0,0 +1,59 @@
+# Copyright 2014-2015 Nathan West
+#
+# This file is part of autocommand.
+#
+# autocommand 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.
+#
+# autocommand 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 autocommand.  If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+from .errors import AutocommandError
+
+
+class AutomainRequiresModuleError(AutocommandError, TypeError):
+    pass
+
+
+def automain(module, *, args=(), kwargs=None):
+    '''
+    This decorator automatically invokes a function if the module is being run
+    as the "__main__" module. Optionally, provide args or kwargs with which to
+    call the function. If `module` is "__main__", the function is called, and
+    the program is `sys.exit`ed with the return value. You can also pass `True`
+    to cause the function to be called unconditionally. If the function is not
+    called, it is returned unchanged by the decorator.
+
+    Usage:
+
+    @automain(__name__)  # Pass __name__ to check __name__=="__main__"
+    def main():
+        ...
+
+    If __name__ is "__main__" here, the main function is called, and then
+    sys.exit called with the return value.
+    '''
+
+    # Check that @automain(...) was called, rather than @automain
+    if callable(module):
+        raise AutomainRequiresModuleError(module)
+
+    if module == '__main__' or module is True:
+        if kwargs is None:
+            kwargs = {}
+
+        # Use a function definition instead of a lambda for a neater traceback
+        def automain_decorator(main):
+            sys.exit(main(*args, **kwargs))
+
+        return automain_decorator
+    else:
+        return lambda main: main
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autoparse.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autoparse.py
new file mode 100644
index 00000000..0276a3fa
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autoparse.py
@@ -0,0 +1,333 @@
+# Copyright 2014-2015 Nathan West
+#
+# This file is part of autocommand.
+#
+# autocommand 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.
+#
+# autocommand 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 autocommand.  If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+from re import compile as compile_regex
+from inspect import signature, getdoc, Parameter
+from argparse import ArgumentParser
+from contextlib import contextmanager
+from functools import wraps
+from io import IOBase
+from autocommand.errors import AutocommandError
+
+
+_empty = Parameter.empty
+
+
+class AnnotationError(AutocommandError):
+    '''Annotation error: annotation must be a string, type, or tuple of both'''
+
+
+class PositionalArgError(AutocommandError):
+    '''
+    Postional Arg Error: autocommand can't handle postional-only parameters
+    '''
+
+
+class KWArgError(AutocommandError):
+    '''kwarg Error: autocommand can't handle a **kwargs parameter'''
+
+
+class DocstringError(AutocommandError):
+    '''Docstring error'''
+
+
+class TooManySplitsError(DocstringError):
+    '''
+    The docstring had too many ---- section splits. Currently we only support
+    using up to a single split, to split the docstring into description and
+    epilog parts.
+    '''
+
+
+def _get_type_description(annotation):
+    '''
+    Given an annotation, return the (type, description) for the parameter.
+    If you provide an annotation that is somehow both a string and a callable,
+    the behavior is undefined.
+    '''
+    if annotation is _empty:
+        return None, None
+    elif callable(annotation):
+        return annotation, None
+    elif isinstance(annotation, str):
+        return None, annotation
+    elif isinstance(annotation, tuple):
+        try:
+            arg1, arg2 = annotation
+        except ValueError as e:
+            raise AnnotationError(annotation) from e
+        else:
+            if callable(arg1) and isinstance(arg2, str):
+                return arg1, arg2
+            elif isinstance(arg1, str) and callable(arg2):
+                return arg2, arg1
+
+    raise AnnotationError(annotation)
+
+
+def _add_arguments(param, parser, used_char_args, add_nos):
+    '''
+    Add the argument(s) to an ArgumentParser (using add_argument) for a given
+    parameter. used_char_args is the set of -short options currently already in
+    use, and is updated (if necessary) by this function. If add_nos is True,
+    this will also add an inverse switch for all boolean options. For
+    instance, for the boolean parameter "verbose", this will create --verbose
+    and --no-verbose.
+    '''
+
+    # Impl note: This function is kept separate from make_parser because it's
+    # already very long and I wanted to separate out as much as possible into
+    # its own call scope, to prevent even the possibility of suble mutation
+    # bugs.
+    if param.kind is param.POSITIONAL_ONLY:
+        raise PositionalArgError(param)
+    elif param.kind is param.VAR_KEYWORD:
+        raise KWArgError(param)
+
+    # These are the kwargs for the add_argument function.
+    arg_spec = {}
+    is_option = False
+
+    # Get the type and default from the annotation.
+    arg_type, description = _get_type_description(param.annotation)
+
+    # Get the default value
+    default = param.default
+
+    # If there is no explicit type, and the default is present and not None,
+    # infer the type from the default.
+    if arg_type is None and default not in {_empty, None}:
+        arg_type = type(default)
+
+    # Add default. The presence of a default means this is an option, not an
+    # argument.
+    if default is not _empty:
+        arg_spec['default'] = default
+        is_option = True
+
+    # Add the type
+    if arg_type is not None:
+        # Special case for bool: make it just a --switch
+        if arg_type is bool:
+            if not default or default is _empty:
+                arg_spec['action'] = 'store_true'
+            else:
+                arg_spec['action'] = 'store_false'
+
+            # Switches are always options
+            is_option = True
+
+        # Special case for file types: make it a string type, for filename
+        elif isinstance(default, IOBase):
+            arg_spec['type'] = str
+
+        # TODO: special case for list type.
+        #   - How to specificy type of list members?
+        #       - param: [int]
+        #       - param: int =[]
+        #   - action='append' vs nargs='*'
+
+        else:
+            arg_spec['type'] = arg_type
+
+    # nargs: if the signature includes *args, collect them as trailing CLI
+    # arguments in a list. *args can't have a default value, so it can never be
+    # an option.
+    if param.kind is param.VAR_POSITIONAL:
+        # TODO: consider depluralizing metavar/name here.
+        arg_spec['nargs'] = '*'
+
+    # Add description.
+    if description is not None:
+        arg_spec['help'] = description
+
+    # Get the --flags
+    flags = []
+    name = param.name
+
+    if is_option:
+        # Add the first letter as a -short option.
+        for letter in name[0], name[0].swapcase():
+            if letter not in used_char_args:
+                used_char_args.add(letter)
+                flags.append('-{}'.format(letter))
+                break
+
+        # If the parameter is a --long option, or is a -short option that
+        # somehow failed to get a flag, add it.
+        if len(name) > 1 or not flags:
+            flags.append('--{}'.format(name))
+
+        arg_spec['dest'] = name
+    else:
+        flags.append(name)
+
+    parser.add_argument(*flags, **arg_spec)
+
+    # Create the --no- version for boolean switches
+    if add_nos and arg_type is bool:
+        parser.add_argument(
+            '--no-{}'.format(name),
+            action='store_const',
+            dest=name,
+            const=default if default is not _empty else False)
+
+
+def make_parser(func_sig, description, epilog, add_nos):
+    '''
+    Given the signature of a function, create an ArgumentParser
+    '''
+    parser = ArgumentParser(description=description, epilog=epilog)
+
+    used_char_args = {'h'}
+
+    # Arange the params so that single-character arguments are first. This
+    # esnures they don't have to get --long versions. sorted is stable, so the
+    # parameters will otherwise still be in relative order.
+    params = sorted(
+        func_sig.parameters.values(),
+        key=lambda param: len(param.name) > 1)
+
+    for param in params:
+        _add_arguments(param, parser, used_char_args, add_nos)
+
+    return parser
+
+
+_DOCSTRING_SPLIT = compile_regex(r'\n\s*-{4,}\s*\n')
+
+
+def parse_docstring(docstring):
+    '''
+    Given a docstring, parse it into a description and epilog part
+    '''
+    if docstring is None:
+        return '', ''
+
+    parts = _DOCSTRING_SPLIT.split(docstring)
+
+    if len(parts) == 1:
+        return docstring, ''
+    elif len(parts) == 2:
+        return parts[0], parts[1]
+    else:
+        raise TooManySplitsError()
+
+
+def autoparse(
+        func=None, *,
+        description=None,
+        epilog=None,
+        add_nos=False,
+        parser=None):
+    '''
+    This decorator converts a function that takes normal arguments into a
+    function which takes a single optional argument, argv, parses it using an
+    argparse.ArgumentParser, and calls the underlying function with the parsed
+    arguments. If it is not given, sys.argv[1:] is used. This is so that the
+    function can be used as a setuptools entry point, as well as a normal main
+    function. sys.argv[1:] is not evaluated until the function is called, to
+    allow injecting different arguments for testing.
+
+    It uses the argument signature of the function to create an
+    ArgumentParser. Parameters without defaults become positional parameters,
+    while parameters *with* defaults become --options. Use annotations to set
+    the type of the parameter.
+
+    The `desctiption` and `epilog` parameters corrospond to the same respective
+    argparse parameters. If no description is given, it defaults to the
+    decorated functions's docstring, if present.
+
+    If add_nos is True, every boolean option (that is, every parameter with a
+    default of True/False or a type of bool) will have a --no- version created
+    as well, which inverts the option. For instance, the --verbose option will
+    have a --no-verbose counterpart. These are not mutually exclusive-
+    whichever one appears last in the argument list will have precedence.
+
+    If a parser is given, it is used instead of one generated from the function
+    signature. In this case, no parser is created; instead, the given parser is
+    used to parse the argv argument. The parser's results' argument names must
+    match up with the parameter names of the decorated function.
+
+    The decorated function is attached to the result as the `func` attribute,
+    and the parser is attached as the `parser` attribute.
+    '''
+
+    # If @autoparse(...) is used instead of @autoparse
+    if func is None:
+        return lambda f: autoparse(
+            f, description=description,
+            epilog=epilog,
+            add_nos=add_nos,
+            parser=parser)
+
+    func_sig = signature(func)
+
+    docstr_description, docstr_epilog = parse_docstring(getdoc(func))
+
+    if parser is None:
+        parser = make_parser(
+            func_sig,
+            description or docstr_description,
+            epilog or docstr_epilog,
+            add_nos)
+
+    @wraps(func)
+    def autoparse_wrapper(argv=None):
+        if argv is None:
+            argv = sys.argv[1:]
+
+        # Get empty argument binding, to fill with parsed arguments. This
+        # object does all the heavy lifting of turning named arguments into
+        # into correctly bound *args and **kwargs.
+        parsed_args = func_sig.bind_partial()
+        parsed_args.arguments.update(vars(parser.parse_args(argv)))
+
+        return func(*parsed_args.args, **parsed_args.kwargs)
+
+    # TODO: attach an updated __signature__ to autoparse_wrapper, just in case.
+
+    # Attach the wrapped function and parser, and return the wrapper.
+    autoparse_wrapper.func = func
+    autoparse_wrapper.parser = parser
+    return autoparse_wrapper
+
+
+@contextmanager
+def smart_open(filename_or_file, *args, **kwargs):
+    '''
+    This context manager allows you to open a filename, if you want to default
+    some already-existing file object, like sys.stdout, which shouldn't be
+    closed at the end of the context. If the filename argument is a str, bytes,
+    or int, the file object is created via a call to open with the given *args
+    and **kwargs, sent to the context, and closed at the end of the context,
+    just like "with open(filename) as f:". If it isn't one of the openable
+    types, the object simply sent to the context unchanged, and left unclosed
+    at the end of the context. Example:
+
+        def work_with_file(name=sys.stdout):
+            with smart_open(name) as f:
+                # Works correctly if name is a str filename or sys.stdout
+                print("Some stuff", file=f)
+                # If it was a filename, f is closed at the end here.
+    '''
+    if isinstance(filename_or_file, (str, bytes, int)):
+        with open(filename_or_file, *args, **kwargs) as file:
+            yield file
+    else:
+        yield filename_or_file
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/errors.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/errors.py
new file mode 100644
index 00000000..25706073
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/errors.py
@@ -0,0 +1,23 @@
+# Copyright 2014-2016 Nathan West
+#
+# This file is part of autocommand.
+#
+# autocommand 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.
+#
+# autocommand 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 autocommand.  If not, see <http://www.gnu.org/licenses/>.
+
+
+class AutocommandError(Exception):
+    '''Base class for autocommand exceptions'''
+    pass
+
+# Individual modules will define errors specific to that module.