diff options
author | CoprDistGit <infra@openeuler.org> | 2023-04-11 03:33:00 +0000 |
---|---|---|
committer | CoprDistGit <infra@openeuler.org> | 2023-04-11 03:33:00 +0000 |
commit | ff0b7f3640448d2741e4ea227bba383bec62d5ce (patch) | |
tree | 17af84ee93452e0e8dadf18a5eeaff41ef8fc334 | |
parent | 015b36c3d7b02660c3520f849c0cc6c329e437f5 (diff) |
automatic import of python-autocommand
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | python-autocommand.spec | 492 | ||||
-rw-r--r-- | sources | 1 |
3 files changed, 494 insertions, 0 deletions
@@ -0,0 +1 @@ +/autocommand-2.2.2.tar.gz diff --git a/python-autocommand.spec b/python-autocommand.spec new file mode 100644 index 0000000..e417a4f --- /dev/null +++ b/python-autocommand.spec @@ -0,0 +1,492 @@ +%global _empty_manifest_terminate_build 0 +Name: python-autocommand +Version: 2.2.2 +Release: 1 +Summary: A library to create a command-line program from a function +License: LGPLv3 +URL: https://github.com/Lucretiel/autocommand +Source0: https://mirrors.nju.edu.cn/pypi/web/packages/5b/18/774bddb96bc0dc0a2b8ac2d2a0e686639744378883da0fc3b96a54192d7a/autocommand-2.2.2.tar.gz +BuildArch: noarch + + +%description + Some extra documentation in the epilog + ''' + with smart_open(infile) as istr: + with smart_open(outfile, 'w') as ostr: + for line in istr: + ostr.write(line) +``` +``` +$ python copy.py -h +usage: copy.py [-h] [-i INFILE] [-o OUTFILE] +Copy an the contents of a file (or stdin) to another file (or stdout) +optional arguments: + -h, --help show this help message and exit + -i INFILE, --infile INFILE + -o OUTFILE, --outfile OUTFILE +Some extra documentation in the epilog +$ echo "Hello World" | python copy.py --outfile hello.txt +$ python copy.py --infile hello.txt --outfile hello2.txt +$ python copy.py --infile hello2.txt +Hello World +``` +### Parameter descriptions +You can also attach description text to individual parameters in the annotation. To attach both a type and a description, supply them both in any order in a tuple +```python +@autocommand(__name__) +def copy_net( + infile: 'The name of the file to send', + host: 'The host to send the file to', + port: (int, 'The port to connect to')): + ''' + Copy a file over raw TCP to a remote destination. + ''' + # Left as an exercise to the reader +``` +### Decorators and wrappers +Autocommand automatically follows wrapper chains created by `@functools.wraps`. This means that you can apply other wrapping decorators to your main function, and autocommand will still correctly detect the signature. +```python +from functools import wraps +from autocommand import autocommand +def print_yielded(func): + ''' + Convert a generator into a function that prints all yielded elements + ''' + @wraps(func) + def wrapper(*args, **kwargs): + for thing in func(*args, **kwargs): + print(thing) + return wrapper +@autocommand(__name__, + description= 'Print all the values from START to STOP, inclusive, in steps of STEP', + epilog= 'STOP and STEP default to 1') +@print_yielded +def seq(stop, start=1, step=1): + for i in range(start, stop + 1, step): + yield i +``` +``` +$ seq.py -h +usage: seq.py [-h] [-s START] [-S STEP] stop +Print all the values from START to STOP, inclusive, in steps of STEP +positional arguments: + stop +optional arguments: + -h, --help show this help message and exit + -s START, --start START + -S STEP, --step STEP +STOP and STEP default to 1 +``` +Even though autocommand is being applied to the `wrapper` returned by `print_yielded`, it still retreives the signature of the underlying `seq` function to create the argument parsing. +### Custom Parser +While autocommand's automatic parser generator is a powerful convenience, it doesn't cover all of the different features that argparse provides. If you need these features, you can provide your own parser as a kwarg to `autocommand`: +```python +from argparse import ArgumentParser +from autocommand import autocommand +parser = ArgumentParser() +# autocommand can't do optional positonal parameters +parser.add_argument('arg', nargs='?') +# or mutually exclusive options +group = parser.add_mutually_exclusive_group() +group.add_argument('-v', '--verbose', action='store_true') +group.add_argument('-q', '--quiet', action='store_true') +@autocommand(__name__, parser=parser) +def main(arg, verbose, quiet): + print(arg, verbose, quiet) +``` +``` +$ python parser.py -h +usage: write_file.py [-h] [-v | -q] [arg] +positional arguments: + arg +optional arguments: + -h, --help show this help message and exit + -v, --verbose + -q, --quiet +$ python parser.py +None False False +$ python parser.py hello +hello False False +$ python parser.py -v +None True False +$ python parser.py -q +None False True +$ python parser.py -vq +usage: parser.py [-h] [-v | -q] [arg] +parser.py: error: argument -q/--quiet: not allowed with argument -v/--verbose +``` +Any parser should work fine, so long as each of the parser's arguments has a corresponding parameter in the decorated main function. The order of parameters doesn't matter, as long as they are all present. Note that when using a custom parser, autocommand doesn't modify the parser or the retrieved arguments. This means that no description/epilog will be added, and the function's type annotations and defaults (if present) will be ignored. +## Testing and Library use +The decorated function is only called and exited from if the first argument to `autocommand` is `'__main__'` or `True`. If it is neither of these values, or no argument is given, then a new main function is created by the decorator. This function has the signature `main(argv=None)`, and is intended to be called with arguments as if via `main(sys.argv[1:])`. The function has the attributes `parser` and `main`, which are the generated `ArgumentParser` and the original main function that was decorated. This is to facilitate testing and library use of your main. Calling the function triggers a `parse_args()` with the supplied arguments, and returns the result of the main function. Note that, while it returns instead of calling `sys.exit`, the `parse_args()` function will raise a `SystemExit` in the event of a parsing error or `-h/--help` argument. +```python + @autocommand() + def test_prog(arg1, arg2: int, quiet=False, verbose=False): + if not quiet: + print(arg1, arg2) + if verbose: + print("LOUD NOISES") + return 0 + print(test_prog(['-v', 'hello', '80'])) +``` +``` +$ python test_prog.py +hello 80 +LOUD NOISES +0 +``` +If the function is called with no arguments, `sys.argv[1:]` is used. This is to allow the autocommand function to be used as a setuptools entry point. +## Exceptions and limitations +- There are a few possible exceptions that `autocommand` can raise. All of them derive from `autocommand.AutocommandError`. + - If an invalid annotation is given (that is, it isn't a `type`, `str`, `(type, str)`, or `(str, type)`, an `AnnotationError` is raised. The `type` may be any callable, as described in the `Types`_ section. + - If the function has a `**kwargs` parameter, a `KWargError` is raised. + - If, somehow, the function has a positional-only parameter, a `PositionalArgError` is raised. This means that the argument doesn't have a name, which is currently not possible with a plain `def` or `lambda`, though many built-in functions have this kind of parameter. +- There are a few argparse features that are not supported by autocommand. + - It isn't possible to have an optional positional argument (as opposed to a `--option`). POSIX thinks this is bad form anyway. + - It isn't possible to have mutually exclusive arguments or options + - It isn't possible to have subcommands or subparsers, though I'm working on a few solutions involving classes or nested function definitions to allow this. +## Development +Autocommand cannot be important from the project root; this is to enforce separation of concerns and prevent accidental importing of `setup.py` or tests. To develop, install the project in editable mode: +``` +$ python setup.py develop +``` +This will create a link to the source files in the deployment directory, so that any source changes are reflected when it is imported. + +%package -n python3-autocommand +Summary: A library to create a command-line program from a function +Provides: python-autocommand +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: python3-pip +%description -n python3-autocommand + Some extra documentation in the epilog + ''' + with smart_open(infile) as istr: + with smart_open(outfile, 'w') as ostr: + for line in istr: + ostr.write(line) +``` +``` +$ python copy.py -h +usage: copy.py [-h] [-i INFILE] [-o OUTFILE] +Copy an the contents of a file (or stdin) to another file (or stdout) +optional arguments: + -h, --help show this help message and exit + -i INFILE, --infile INFILE + -o OUTFILE, --outfile OUTFILE +Some extra documentation in the epilog +$ echo "Hello World" | python copy.py --outfile hello.txt +$ python copy.py --infile hello.txt --outfile hello2.txt +$ python copy.py --infile hello2.txt +Hello World +``` +### Parameter descriptions +You can also attach description text to individual parameters in the annotation. To attach both a type and a description, supply them both in any order in a tuple +```python +@autocommand(__name__) +def copy_net( + infile: 'The name of the file to send', + host: 'The host to send the file to', + port: (int, 'The port to connect to')): + ''' + Copy a file over raw TCP to a remote destination. + ''' + # Left as an exercise to the reader +``` +### Decorators and wrappers +Autocommand automatically follows wrapper chains created by `@functools.wraps`. This means that you can apply other wrapping decorators to your main function, and autocommand will still correctly detect the signature. +```python +from functools import wraps +from autocommand import autocommand +def print_yielded(func): + ''' + Convert a generator into a function that prints all yielded elements + ''' + @wraps(func) + def wrapper(*args, **kwargs): + for thing in func(*args, **kwargs): + print(thing) + return wrapper +@autocommand(__name__, + description= 'Print all the values from START to STOP, inclusive, in steps of STEP', + epilog= 'STOP and STEP default to 1') +@print_yielded +def seq(stop, start=1, step=1): + for i in range(start, stop + 1, step): + yield i +``` +``` +$ seq.py -h +usage: seq.py [-h] [-s START] [-S STEP] stop +Print all the values from START to STOP, inclusive, in steps of STEP +positional arguments: + stop +optional arguments: + -h, --help show this help message and exit + -s START, --start START + -S STEP, --step STEP +STOP and STEP default to 1 +``` +Even though autocommand is being applied to the `wrapper` returned by `print_yielded`, it still retreives the signature of the underlying `seq` function to create the argument parsing. +### Custom Parser +While autocommand's automatic parser generator is a powerful convenience, it doesn't cover all of the different features that argparse provides. If you need these features, you can provide your own parser as a kwarg to `autocommand`: +```python +from argparse import ArgumentParser +from autocommand import autocommand +parser = ArgumentParser() +# autocommand can't do optional positonal parameters +parser.add_argument('arg', nargs='?') +# or mutually exclusive options +group = parser.add_mutually_exclusive_group() +group.add_argument('-v', '--verbose', action='store_true') +group.add_argument('-q', '--quiet', action='store_true') +@autocommand(__name__, parser=parser) +def main(arg, verbose, quiet): + print(arg, verbose, quiet) +``` +``` +$ python parser.py -h +usage: write_file.py [-h] [-v | -q] [arg] +positional arguments: + arg +optional arguments: + -h, --help show this help message and exit + -v, --verbose + -q, --quiet +$ python parser.py +None False False +$ python parser.py hello +hello False False +$ python parser.py -v +None True False +$ python parser.py -q +None False True +$ python parser.py -vq +usage: parser.py [-h] [-v | -q] [arg] +parser.py: error: argument -q/--quiet: not allowed with argument -v/--verbose +``` +Any parser should work fine, so long as each of the parser's arguments has a corresponding parameter in the decorated main function. The order of parameters doesn't matter, as long as they are all present. Note that when using a custom parser, autocommand doesn't modify the parser or the retrieved arguments. This means that no description/epilog will be added, and the function's type annotations and defaults (if present) will be ignored. +## Testing and Library use +The decorated function is only called and exited from if the first argument to `autocommand` is `'__main__'` or `True`. If it is neither of these values, or no argument is given, then a new main function is created by the decorator. This function has the signature `main(argv=None)`, and is intended to be called with arguments as if via `main(sys.argv[1:])`. The function has the attributes `parser` and `main`, which are the generated `ArgumentParser` and the original main function that was decorated. This is to facilitate testing and library use of your main. Calling the function triggers a `parse_args()` with the supplied arguments, and returns the result of the main function. Note that, while it returns instead of calling `sys.exit`, the `parse_args()` function will raise a `SystemExit` in the event of a parsing error or `-h/--help` argument. +```python + @autocommand() + def test_prog(arg1, arg2: int, quiet=False, verbose=False): + if not quiet: + print(arg1, arg2) + if verbose: + print("LOUD NOISES") + return 0 + print(test_prog(['-v', 'hello', '80'])) +``` +``` +$ python test_prog.py +hello 80 +LOUD NOISES +0 +``` +If the function is called with no arguments, `sys.argv[1:]` is used. This is to allow the autocommand function to be used as a setuptools entry point. +## Exceptions and limitations +- There are a few possible exceptions that `autocommand` can raise. All of them derive from `autocommand.AutocommandError`. + - If an invalid annotation is given (that is, it isn't a `type`, `str`, `(type, str)`, or `(str, type)`, an `AnnotationError` is raised. The `type` may be any callable, as described in the `Types`_ section. + - If the function has a `**kwargs` parameter, a `KWargError` is raised. + - If, somehow, the function has a positional-only parameter, a `PositionalArgError` is raised. This means that the argument doesn't have a name, which is currently not possible with a plain `def` or `lambda`, though many built-in functions have this kind of parameter. +- There are a few argparse features that are not supported by autocommand. + - It isn't possible to have an optional positional argument (as opposed to a `--option`). POSIX thinks this is bad form anyway. + - It isn't possible to have mutually exclusive arguments or options + - It isn't possible to have subcommands or subparsers, though I'm working on a few solutions involving classes or nested function definitions to allow this. +## Development +Autocommand cannot be important from the project root; this is to enforce separation of concerns and prevent accidental importing of `setup.py` or tests. To develop, install the project in editable mode: +``` +$ python setup.py develop +``` +This will create a link to the source files in the deployment directory, so that any source changes are reflected when it is imported. + +%package help +Summary: Development documents and examples for autocommand +Provides: python3-autocommand-doc +%description help + Some extra documentation in the epilog + ''' + with smart_open(infile) as istr: + with smart_open(outfile, 'w') as ostr: + for line in istr: + ostr.write(line) +``` +``` +$ python copy.py -h +usage: copy.py [-h] [-i INFILE] [-o OUTFILE] +Copy an the contents of a file (or stdin) to another file (or stdout) +optional arguments: + -h, --help show this help message and exit + -i INFILE, --infile INFILE + -o OUTFILE, --outfile OUTFILE +Some extra documentation in the epilog +$ echo "Hello World" | python copy.py --outfile hello.txt +$ python copy.py --infile hello.txt --outfile hello2.txt +$ python copy.py --infile hello2.txt +Hello World +``` +### Parameter descriptions +You can also attach description text to individual parameters in the annotation. To attach both a type and a description, supply them both in any order in a tuple +```python +@autocommand(__name__) +def copy_net( + infile: 'The name of the file to send', + host: 'The host to send the file to', + port: (int, 'The port to connect to')): + ''' + Copy a file over raw TCP to a remote destination. + ''' + # Left as an exercise to the reader +``` +### Decorators and wrappers +Autocommand automatically follows wrapper chains created by `@functools.wraps`. This means that you can apply other wrapping decorators to your main function, and autocommand will still correctly detect the signature. +```python +from functools import wraps +from autocommand import autocommand +def print_yielded(func): + ''' + Convert a generator into a function that prints all yielded elements + ''' + @wraps(func) + def wrapper(*args, **kwargs): + for thing in func(*args, **kwargs): + print(thing) + return wrapper +@autocommand(__name__, + description= 'Print all the values from START to STOP, inclusive, in steps of STEP', + epilog= 'STOP and STEP default to 1') +@print_yielded +def seq(stop, start=1, step=1): + for i in range(start, stop + 1, step): + yield i +``` +``` +$ seq.py -h +usage: seq.py [-h] [-s START] [-S STEP] stop +Print all the values from START to STOP, inclusive, in steps of STEP +positional arguments: + stop +optional arguments: + -h, --help show this help message and exit + -s START, --start START + -S STEP, --step STEP +STOP and STEP default to 1 +``` +Even though autocommand is being applied to the `wrapper` returned by `print_yielded`, it still retreives the signature of the underlying `seq` function to create the argument parsing. +### Custom Parser +While autocommand's automatic parser generator is a powerful convenience, it doesn't cover all of the different features that argparse provides. If you need these features, you can provide your own parser as a kwarg to `autocommand`: +```python +from argparse import ArgumentParser +from autocommand import autocommand +parser = ArgumentParser() +# autocommand can't do optional positonal parameters +parser.add_argument('arg', nargs='?') +# or mutually exclusive options +group = parser.add_mutually_exclusive_group() +group.add_argument('-v', '--verbose', action='store_true') +group.add_argument('-q', '--quiet', action='store_true') +@autocommand(__name__, parser=parser) +def main(arg, verbose, quiet): + print(arg, verbose, quiet) +``` +``` +$ python parser.py -h +usage: write_file.py [-h] [-v | -q] [arg] +positional arguments: + arg +optional arguments: + -h, --help show this help message and exit + -v, --verbose + -q, --quiet +$ python parser.py +None False False +$ python parser.py hello +hello False False +$ python parser.py -v +None True False +$ python parser.py -q +None False True +$ python parser.py -vq +usage: parser.py [-h] [-v | -q] [arg] +parser.py: error: argument -q/--quiet: not allowed with argument -v/--verbose +``` +Any parser should work fine, so long as each of the parser's arguments has a corresponding parameter in the decorated main function. The order of parameters doesn't matter, as long as they are all present. Note that when using a custom parser, autocommand doesn't modify the parser or the retrieved arguments. This means that no description/epilog will be added, and the function's type annotations and defaults (if present) will be ignored. +## Testing and Library use +The decorated function is only called and exited from if the first argument to `autocommand` is `'__main__'` or `True`. If it is neither of these values, or no argument is given, then a new main function is created by the decorator. This function has the signature `main(argv=None)`, and is intended to be called with arguments as if via `main(sys.argv[1:])`. The function has the attributes `parser` and `main`, which are the generated `ArgumentParser` and the original main function that was decorated. This is to facilitate testing and library use of your main. Calling the function triggers a `parse_args()` with the supplied arguments, and returns the result of the main function. Note that, while it returns instead of calling `sys.exit`, the `parse_args()` function will raise a `SystemExit` in the event of a parsing error or `-h/--help` argument. +```python + @autocommand() + def test_prog(arg1, arg2: int, quiet=False, verbose=False): + if not quiet: + print(arg1, arg2) + if verbose: + print("LOUD NOISES") + return 0 + print(test_prog(['-v', 'hello', '80'])) +``` +``` +$ python test_prog.py +hello 80 +LOUD NOISES +0 +``` +If the function is called with no arguments, `sys.argv[1:]` is used. This is to allow the autocommand function to be used as a setuptools entry point. +## Exceptions and limitations +- There are a few possible exceptions that `autocommand` can raise. All of them derive from `autocommand.AutocommandError`. + - If an invalid annotation is given (that is, it isn't a `type`, `str`, `(type, str)`, or `(str, type)`, an `AnnotationError` is raised. The `type` may be any callable, as described in the `Types`_ section. + - If the function has a `**kwargs` parameter, a `KWargError` is raised. + - If, somehow, the function has a positional-only parameter, a `PositionalArgError` is raised. This means that the argument doesn't have a name, which is currently not possible with a plain `def` or `lambda`, though many built-in functions have this kind of parameter. +- There are a few argparse features that are not supported by autocommand. + - It isn't possible to have an optional positional argument (as opposed to a `--option`). POSIX thinks this is bad form anyway. + - It isn't possible to have mutually exclusive arguments or options + - It isn't possible to have subcommands or subparsers, though I'm working on a few solutions involving classes or nested function definitions to allow this. +## Development +Autocommand cannot be important from the project root; this is to enforce separation of concerns and prevent accidental importing of `setup.py` or tests. To develop, install the project in editable mode: +``` +$ python setup.py develop +``` +This will create a link to the source files in the deployment directory, so that any source changes are reflected when it is imported. + +%prep +%autosetup -n autocommand-2.2.2 + +%build +%py3_build + +%install +%py3_install +install -d -m755 %{buildroot}/%{_pkgdocdir} +if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi +if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi +if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi +if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi +pushd %{buildroot} +if [ -d usr/lib ]; then + find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/lib64 ]; then + find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/bin ]; then + find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/sbin ]; then + find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst +fi +touch doclist.lst +if [ -d usr/share/man ]; then + find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst +fi +popd +mv %{buildroot}/filelist.lst . +mv %{buildroot}/doclist.lst . + +%files -n python3-autocommand -f filelist.lst +%dir %{python3_sitelib}/* + +%files help -f doclist.lst +%{_docdir}/* + +%changelog +* Tue Apr 11 2023 Python_Bot <Python_Bot@openeuler.org> - 2.2.2-1 +- Package Spec generated @@ -0,0 +1 @@ +0cab5141bad0dfb363b086e93fd4125e autocommand-2.2.2.tar.gz |