From 3bcd4f3f6deab3ed3ea6fc57a8d403179285550f Mon Sep 17 00:00:00 2001 From: yage Date: Sun, 18 Feb 2024 02:30:58 -0700 Subject: [PATCH] refactor py/lib into python-scwrypts subproject --- py/data/convert/csv-to-json.py | 20 +- py/data/convert/csv-to-yaml.py | 20 +- py/data/convert/json-to-csv.py | 20 +- py/data/convert/json-to-yaml.py | 20 +- py/data/convert/yaml-to-csv.py | 20 +- py/data/convert/yaml-to-json.py | 20 +- py/directus/get-items.py | 101 ++++---- py/discord/post-message.py | 70 +++-- py/hello-world.py | 30 +-- py/lib/.gitignore | 1 + py/lib/README.md | 3 + py/lib/__init__.py | 6 - py/lib/data/__init__.py | 1 - py/lib/fzf/__init__.py | 1 - py/lib/http/__init__.py | 5 - py/lib/http/directus/__init__.py | 2 - py/lib/http/discord/__init__.py | 2 - py/lib/http/linear/__init__.py | 1 - py/lib/pyproject.toml | 61 +++++ py/lib/redis/__init__.py | 1 - py/lib/scwrypts/__init__.py | 13 +- py/lib/scwrypts/data/__init__.py | 1 + py/lib/{ => scwrypts}/data/converter.py | 3 - py/lib/scwrypts/data/test_converter.py | 75 ++++++ py/lib/scwrypts/{getenv.py => env.py} | 2 +- py/lib/scwrypts/fzf/__init__.py | 1 + py/lib/{ => scwrypts}/fzf/client.py | 0 py/lib/scwrypts/http/__init__.py | 1 + py/lib/{ => scwrypts}/http/client.py | 0 .../http/directus/__init__.py} | 2 + py/lib/{ => scwrypts}/http/directus/client.py | 5 +- py/lib/scwrypts/http/discord/__init__.py | 2 + py/lib/{ => scwrypts}/http/discord/client.py | 4 +- .../http/discord/send_message.py | 4 +- py/lib/scwrypts/http/linear/__init__.py | 1 + py/lib/{ => scwrypts}/http/linear/client.py | 5 +- py/lib/scwrypts/io/__init__.py | 1 + .../{io.py => io/combined_io_stream.py} | 8 +- py/lib/scwrypts/redis/__init__.py | 1 + py/lib/{ => scwrypts}/redis/client.py | 2 +- py/lib/scwrypts/run.py | 22 -- py/lib/scwrypts/scwrypts/__init__.py | 0 py/lib/scwrypts/{ => scwrypts}/exceptions.py | 10 +- py/lib/scwrypts/{ => scwrypts}/execute.py | 9 +- py/lib/scwrypts/{ => scwrypts}/interactive.py | 3 + py/lib/scwrypts/scwrypts/scwrypts.py | 30 +++ py/lib/scwrypts/test/__init__.py | 1 + py/lib/scwrypts/test/exceptions.py | 10 + py/lib/scwrypts/test/random_generator.py | 240 ++++++++++++++++++ py/lib/scwrypts/test/test_random_generator.py | 44 ++++ py/lib/scwrypts/twilio/__init__.py | 2 + py/lib/{ => scwrypts}/twilio/client.py | 2 +- py/lib/{ => scwrypts}/twilio/send_sms.py | 2 +- py/lib/twilio/__init__.py | 2 - py/linear/comment.py | 43 ++-- py/redis/interactive.py | 21 +- py/requirements.txt | 6 +- py/twilio/send-sms.py | 67 +++-- 58 files changed, 719 insertions(+), 331 deletions(-) create mode 100644 py/lib/.gitignore create mode 100644 py/lib/README.md delete mode 100644 py/lib/__init__.py delete mode 100644 py/lib/data/__init__.py delete mode 100644 py/lib/fzf/__init__.py delete mode 100644 py/lib/http/__init__.py delete mode 100644 py/lib/http/directus/__init__.py delete mode 100644 py/lib/http/discord/__init__.py delete mode 100644 py/lib/http/linear/__init__.py create mode 100644 py/lib/pyproject.toml delete mode 100644 py/lib/redis/__init__.py create mode 100644 py/lib/scwrypts/data/__init__.py rename py/lib/{ => scwrypts}/data/converter.py (94%) create mode 100644 py/lib/scwrypts/data/test_converter.py rename py/lib/scwrypts/{getenv.py => env.py} (79%) create mode 100644 py/lib/scwrypts/fzf/__init__.py rename py/lib/{ => scwrypts}/fzf/client.py (100%) create mode 100644 py/lib/scwrypts/http/__init__.py rename py/lib/{ => scwrypts}/http/client.py (100%) rename py/lib/{http/directus/constant.py => scwrypts/http/directus/__init__.py} (95%) rename py/lib/{ => scwrypts}/http/directus/client.py (94%) create mode 100644 py/lib/scwrypts/http/discord/__init__.py rename py/lib/{ => scwrypts}/http/discord/client.py (86%) rename py/lib/{ => scwrypts}/http/discord/send_message.py (95%) create mode 100644 py/lib/scwrypts/http/linear/__init__.py rename py/lib/{ => scwrypts}/http/linear/client.py (86%) create mode 100644 py/lib/scwrypts/io/__init__.py rename py/lib/scwrypts/{io.py => io/combined_io_stream.py} (94%) create mode 100644 py/lib/scwrypts/redis/__init__.py rename py/lib/{ => scwrypts}/redis/client.py (92%) delete mode 100644 py/lib/scwrypts/run.py create mode 100644 py/lib/scwrypts/scwrypts/__init__.py rename py/lib/scwrypts/{ => scwrypts}/exceptions.py (75%) rename py/lib/scwrypts/{ => scwrypts}/execute.py (64%) rename py/lib/scwrypts/{ => scwrypts}/interactive.py (83%) create mode 100644 py/lib/scwrypts/scwrypts/scwrypts.py create mode 100644 py/lib/scwrypts/test/__init__.py create mode 100644 py/lib/scwrypts/test/exceptions.py create mode 100644 py/lib/scwrypts/test/random_generator.py create mode 100644 py/lib/scwrypts/test/test_random_generator.py create mode 100644 py/lib/scwrypts/twilio/__init__.py rename py/lib/{ => scwrypts}/twilio/client.py (92%) rename py/lib/{ => scwrypts}/twilio/send_sms.py (97%) delete mode 100644 py/lib/twilio/__init__.py diff --git a/py/data/convert/csv-to-json.py b/py/data/convert/csv-to-json.py index 7b8fffc..2cfe40c 100755 --- a/py/data/convert/csv-to-json.py +++ b/py/data/convert/csv-to-json.py @@ -1,13 +1,11 @@ #!/usr/bin/env python -from py.lib.data.converter import convert -from py.lib.scwrypts import execute - -from py.lib.scwrypts.exceptions import ImportedExecutableError - -if __name__ != '__main__': - raise ImportedExecutableError() - +from scwrypts import execute ##################################################################### +from scwrypts.data import convert + + +description = 'convert csv into json' +parse_args = [] def main(_args, stream): return convert( @@ -18,7 +16,5 @@ def main(_args, stream): ) ##################################################################### -execute(main, - description = 'convert csv into json', - parse_args = [], - ) +if __name__ == '__main__': + execute(main, description, parse_args) diff --git a/py/data/convert/csv-to-yaml.py b/py/data/convert/csv-to-yaml.py index 2d738d4..572eb1e 100755 --- a/py/data/convert/csv-to-yaml.py +++ b/py/data/convert/csv-to-yaml.py @@ -1,13 +1,11 @@ #!/usr/bin/env python -from py.lib.data.converter import convert -from py.lib.scwrypts import execute - -from py.lib.scwrypts.exceptions import ImportedExecutableError - -if __name__ != '__main__': - raise ImportedExecutableError() - +from scwrypts import execute ##################################################################### +from scwrypts.data import convert + + +description = 'convert csv into yaml' +parse_args = [] def main(_args, stream): return convert( @@ -18,7 +16,5 @@ def main(_args, stream): ) ##################################################################### -execute(main, - description = 'convert csv into yaml', - parse_args = [], - ) +if __name__ == '__main__': + execute(main, description, parse_args) diff --git a/py/data/convert/json-to-csv.py b/py/data/convert/json-to-csv.py index 5663735..5383412 100755 --- a/py/data/convert/json-to-csv.py +++ b/py/data/convert/json-to-csv.py @@ -1,13 +1,11 @@ #!/usr/bin/env python -from py.lib.data.converter import convert -from py.lib.scwrypts import execute - -from py.lib.scwrypts.exceptions import ImportedExecutableError - -if __name__ != '__main__': - raise ImportedExecutableError() - +from scwrypts import execute ##################################################################### +from scwrypts.data import convert + + +description = 'convert json into csv' +parse_args = [] def main(_args, stream): return convert( @@ -18,7 +16,5 @@ def main(_args, stream): ) ##################################################################### -execute(main, - description = 'convert json into csv', - parse_args = [], - ) +if __name__ == '__main__': + execute(main, description, parse_args) diff --git a/py/data/convert/json-to-yaml.py b/py/data/convert/json-to-yaml.py index 492581c..22028ca 100755 --- a/py/data/convert/json-to-yaml.py +++ b/py/data/convert/json-to-yaml.py @@ -1,13 +1,11 @@ #!/usr/bin/env python -from py.lib.data.converter import convert -from py.lib.scwrypts import execute - -from py.lib.scwrypts.exceptions import ImportedExecutableError - -if __name__ != '__main__': - raise ImportedExecutableError() - +from scwrypts import execute ##################################################################### +from scwrypts.data import convert + + +description = 'convert json into yaml' +parse_args = [] def main(_args, stream): return convert( @@ -18,7 +16,5 @@ def main(_args, stream): ) ##################################################################### -execute(main, - description = 'convert json into yaml', - parse_args = [], - ) +if __name__ == '__main__': + execute(main, description, parse_args) diff --git a/py/data/convert/yaml-to-csv.py b/py/data/convert/yaml-to-csv.py index ae8b0c9..d7ae838 100755 --- a/py/data/convert/yaml-to-csv.py +++ b/py/data/convert/yaml-to-csv.py @@ -1,13 +1,11 @@ #!/usr/bin/env python -from py.lib.data.converter import convert -from py.lib.scwrypts import execute - -from py.lib.scwrypts.exceptions import ImportedExecutableError - -if __name__ != '__main__': - raise ImportedExecutableError() - +from scwrypts import execute ##################################################################### +from scwrypts.data import convert + + +description = 'convert yaml into csv' +parse_args = [] def main(_args, stream): return convert( @@ -18,7 +16,5 @@ def main(_args, stream): ) ##################################################################### -execute(main, - description = 'convert yaml into csv', - parse_args = [], - ) +if __name__ == '__main__': + execute(main, description, parse_args) diff --git a/py/data/convert/yaml-to-json.py b/py/data/convert/yaml-to-json.py index 31610e6..0eaa4ab 100755 --- a/py/data/convert/yaml-to-json.py +++ b/py/data/convert/yaml-to-json.py @@ -1,13 +1,11 @@ #!/usr/bin/env python -from py.lib.data.converter import convert -from py.lib.scwrypts import execute - -from py.lib.scwrypts.exceptions import ImportedExecutableError - -if __name__ != '__main__': - raise ImportedExecutableError() - +from scwrypts import execute ##################################################################### +from scwrypts.data import convert + + +description = 'convert yaml into json' +parse_args = [] def main(_args, stream): return convert( @@ -18,7 +16,5 @@ def main(_args, stream): ) ##################################################################### -execute(main, - description = 'convert yaml into json', - parse_args = [], - ) +if __name__ == '__main__': + execute(main, description, parse_args) diff --git a/py/directus/get-items.py b/py/directus/get-items.py index 3f76f9b..858c148 100755 --- a/py/directus/get-items.py +++ b/py/directus/get-items.py @@ -1,16 +1,55 @@ #!/usr/bin/env python +from scwrypts import execute +##################################################################### from json import dumps -from py.lib.fzf import fzf, fzf_tail -from py.lib.http import directus -from py.lib.scwrypts import execute +from scwrypts.fzf import fzf, fzf_tail +from scwrypts.http import directus -from py.lib.scwrypts.exceptions import ImportedExecutableError -if __name__ != '__main__': - raise ImportedExecutableError() +description = 'interactive CLI to get data from directus' -##################################################################### +parse_args = [ + ( ['-c', '--collection'], { + "dest" : 'collection', + "default" : None, + "help" : 'the name of the collection', + "required" : False, + }), + ( ['-f', '--filters'], { + "dest" : 'filters', + "default" : None, + "help" : 'as a URL-suffix, filters for the query', + "required" : False, + }), + ( ['-d', '--fields'], { + "dest" : 'fields', + "default" : None, + "help" : 'comma-separated list of fields to include', + "required" : False, + }), + ( ['-p', '--interactive-prompt'], { + "action" : 'store_true', + "dest" : 'interactive', + "default" : False, + "help" : 'interactively generate filter prompts; implied if no flags are provided', + "required" : False, + }), + ( ['--prompt-filters'], { + "action" : 'store_true', + "dest" : 'generate_filters_prompt', + "default" : False, + "help" : '(superceded by -p) only generate filters interactively', + "required" : False, + }), + ( ['--prompt-fields'], { + "action" : 'store_true', + "dest" : 'generate_fields_prompt', + "default" : False, + "help" : '(superceded by -p) only generate filters interactively', + "required" : False, + }), + ] def main(args, stream): if {None} == { args.collection, args.filters, args.fields }: @@ -96,50 +135,6 @@ def _get_or_select_fields(args, collection): return fields - ##################################################################### -execute(main, - description = 'interactive CLI to get data from directus', - parse_args = [ - ( ['-c', '--collection'], { - "dest" : 'collection', - "default" : None, - "help" : 'the name of the collection', - "required" : False, - }), - ( ['-f', '--filters'], { - "dest" : 'filters', - "default" : None, - "help" : 'as a URL-suffix, filters for the query', - "required" : False, - }), - ( ['-d', '--fields'], { - "dest" : 'fields', - "default" : None, - "help" : 'comma-separated list of fields to include', - "required" : False, - }), - ( ['-p', '--interactive-prompt'], { - "action" : 'store_true', - "dest" : 'interactive', - "default" : False, - "help" : 'interactively generate filter prompts; implied if no flags are provided', - "required" : False, - }), - ( ['--prompt-filters'], { - "action" : 'store_true', - "dest" : 'generate_filters_prompt', - "default" : False, - "help" : '(superceded by -p) only generate filters interactively', - "required" : False, - }), - ( ['--prompt-fields'], { - "action" : 'store_true', - "dest" : 'generate_fields_prompt', - "default" : False, - "help" : '(superceded by -p) only generate filters interactively', - "required" : False, - }), - ] - - ) +if __name__ == '__main__': + execute(main, description, parse_args) diff --git a/py/discord/post-message.py b/py/discord/post-message.py index 88b5f5b..609efd6 100755 --- a/py/discord/post-message.py +++ b/py/discord/post-message.py @@ -1,16 +1,40 @@ #!/usr/bin/env python +from scwrypts import execute +##################################################################### from json import dumps from sys import stderr -from py.lib.http import discord -from py.lib.scwrypts import execute +from scwrypts.http import discord -from py.lib.scwrypts.exceptions import ImportedExecutableError -if __name__ != '__main__': - raise ImportedExecutableError() - -##################################################################### +description = 'post a message to the indicated discord channel' +parse_args = [ + ( ['-b', '--body'], { + 'dest' : 'content', + 'help' : 'message body', + 'required' : False, + }), + ( ['-c', '--channel-id'], { + 'dest' : 'channel_id', + 'help' : 'override default target channel id', + 'required' : False, + }), + ( ['-w', '--webhook'], { + 'dest' : 'webhook', + 'help' : 'override default target webhook (takes precedence over -c)', + 'required' : False, + }), + ( ['--avatar-url'], { + 'dest' : 'avatar_url', + 'help' : 'override default avatar_url', + 'required' : False, + }), + ( ['--username'], { + 'dest' : 'username', + 'help' : 'override default username', + 'required' : False, + }), + ] def main(args, stream): if args.content is None: @@ -29,33 +53,5 @@ def main(args, stream): ##################################################################### -execute(main, - description = 'post a message to the indicated discord channel', - parse_args = [ - ( ['-b', '--body'], { - 'dest' : 'content', - 'help' : 'message body', - 'required' : False, - }), - ( ['-c', '--channel-id'], { - 'dest' : 'channel_id', - 'help' : 'override default target channel id', - 'required' : False, - }), - ( ['-w', '--webhook'], { - 'dest' : 'webhook', - 'help' : 'override default target webhook (takes precedence over -c)', - 'required' : False, - }), - ( ['--avatar-url'], { - 'dest' : 'avatar_url', - 'help' : 'override default avatar_url', - 'required' : False, - }), - ( ['--username'], { - 'dest' : 'username', - 'help' : 'override default username', - 'required' : False, - }), - ] - ) +if __name__ == '__main__': + execute(main, description, parse_args) diff --git a/py/hello-world.py b/py/hello-world.py index 2284ae5..b1d7ec7 100755 --- a/py/hello-world.py +++ b/py/hello-world.py @@ -1,27 +1,21 @@ #!/usr/bin/env python -from py.lib.scwrypts import execute - -from py.lib.scwrypts.exceptions import ImportedExecutableError - -if __name__ != '__main__': - raise ImportedExecutableError() - +from scwrypts import execute ##################################################################### +description = 'a simple "Hello, World!" program' +parse_args = [ + ( ['-m', '--message'], { + 'dest' : 'message', + 'default' : 'HELLO WORLD', + 'help' : 'message to print', + 'required' : False, + }), + ] def main(args, stream): stream.writeline(args.message) ##################################################################### -execute(main, - description = 'a simple "Hello, World!" program', - parse_args = [ - ( ['-m', '--message'], { - 'dest' : 'message', - 'default' : 'HELLO WORLD', - 'help' : 'message to print', - 'required' : False, - }), - ], - ) +if __name__ == '__main__': + execute(main, description, parse_args) diff --git a/py/lib/.gitignore b/py/lib/.gitignore new file mode 100644 index 0000000..849ddff --- /dev/null +++ b/py/lib/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/py/lib/README.md b/py/lib/README.md new file mode 100644 index 0000000..203e817 --- /dev/null +++ b/py/lib/README.md @@ -0,0 +1,3 @@ +# Python Scwrypts +[![Generic Badge](https://img.shields.io/badge/python->=3.9-informational.svg)](https://python.org) +
diff --git a/py/lib/__init__.py b/py/lib/__init__.py deleted file mode 100644 index 21a4809..0000000 --- a/py/lib/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -import py.lib.data -import py.lib.fzf -import py.lib.http -import py.lib.redis -import py.lib.scwrypts -import py.lib.twilio diff --git a/py/lib/data/__init__.py b/py/lib/data/__init__.py deleted file mode 100644 index 374d0d2..0000000 --- a/py/lib/data/__init__.py +++ /dev/null @@ -1 +0,0 @@ -import py.lib.data.converter diff --git a/py/lib/fzf/__init__.py b/py/lib/fzf/__init__.py deleted file mode 100644 index bfd1043..0000000 --- a/py/lib/fzf/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from py.lib.fzf.client import fzf, fzf_tail, fzf_head diff --git a/py/lib/http/__init__.py b/py/lib/http/__init__.py deleted file mode 100644 index 73d515a..0000000 --- a/py/lib/http/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from py.lib.http.client import get_request_client - -import py.lib.http.directus -import py.lib.http.discord -import py.lib.http.linear diff --git a/py/lib/http/directus/__init__.py b/py/lib/http/directus/__init__.py deleted file mode 100644 index 1355255..0000000 --- a/py/lib/http/directus/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from py.lib.http.directus.client import * -from py.lib.http.directus.constant import * diff --git a/py/lib/http/discord/__init__.py b/py/lib/http/discord/__init__.py deleted file mode 100644 index 0935e8c..0000000 --- a/py/lib/http/discord/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from py.lib.http.discord.client import * -from py.lib.http.discord.send_message import * diff --git a/py/lib/http/linear/__init__.py b/py/lib/http/linear/__init__.py deleted file mode 100644 index 670a494..0000000 --- a/py/lib/http/linear/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from py.lib.http.linear.client import * diff --git a/py/lib/pyproject.toml b/py/lib/pyproject.toml new file mode 100644 index 0000000..58ab17c --- /dev/null +++ b/py/lib/pyproject.toml @@ -0,0 +1,61 @@ +[project] +name = 'scwrypts' +description = 'scwrypts library and invoker' +license = 'GPL-3.0-or-later' + +readme = 'README.md' +requires-python = '>=3.10' + +authors = [ + { name='yage', email='yage@yage.io' }, +] + + +classifiers = [ + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', + ] + +dynamic = ['version'] + +dependencies = [ + 'bpython', + 'pyfzf', + 'pyyaml', + 'redis', + 'twilio', +] + +[project.optional-dependencies] +dev = [ + 'pylint', +] + +test = [ + 'pytest', + 'mergedeep', +] + +[project.urls] +homepage = 'https://github.com/wrynegade/scwrypts' +issues = 'https://github.com/wrynegade/scwrypts/issues' + +[build-system] +requires = [ + 'hatchling', + 'versioningit', +] +build-backend = 'hatchling.build' + +[tool.hatch.version] +source = 'versioningit' + +[tool.hatch.build.targets.wheel] +packages = ['./'] + +[tool.versioningit] +match = ['v*'] + diff --git a/py/lib/redis/__init__.py b/py/lib/redis/__init__.py deleted file mode 100644 index 248af1f..0000000 --- a/py/lib/redis/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from py.lib.redis.client import get_client diff --git a/py/lib/scwrypts/__init__.py b/py/lib/scwrypts/__init__.py index 0cc3adf..e28bd49 100644 --- a/py/lib/scwrypts/__init__.py +++ b/py/lib/scwrypts/__init__.py @@ -1,6 +1,9 @@ -from py.lib.scwrypts.execute import execute -from py.lib.scwrypts.getenv import getenv -from py.lib.scwrypts.interactive import interactive -from py.lib.scwrypts.run import run +''' +scwrypts -import py.lib.scwrypts.io +python library functions and invoker for scwrypts +''' + +from .scwrypts.execute import execute +from .scwrypts.interactive import interactive +from .scwrypts.scwrypts import scwrypts diff --git a/py/lib/scwrypts/data/__init__.py b/py/lib/scwrypts/data/__init__.py new file mode 100644 index 0000000..68692bd --- /dev/null +++ b/py/lib/scwrypts/data/__init__.py @@ -0,0 +1 @@ +from .converter import convert diff --git a/py/lib/data/converter.py b/py/lib/scwrypts/data/converter.py similarity index 94% rename from py/lib/data/converter.py rename to py/lib/scwrypts/data/converter.py index 82d9154..8f772a5 100644 --- a/py/lib/data/converter.py +++ b/py/lib/scwrypts/data/converter.py @@ -4,9 +4,6 @@ import yaml def convert(input_stream, input_type, output_stream, output_type): - if input_type == output_type: - raise ValueError('input type and output type are the same') - data = convert_input(input_stream, input_type) write_output(output_stream, output_type, data) diff --git a/py/lib/scwrypts/data/test_converter.py b/py/lib/scwrypts/data/test_converter.py new file mode 100644 index 0000000..41e907a --- /dev/null +++ b/py/lib/scwrypts/data/test_converter.py @@ -0,0 +1,75 @@ +from io import StringIO +#from string import ascii_letters, digits +from unittest.mock import patch + +from pytest import raises + +from scwrypts.test import generate + +from .converter import convert + +GENERATE_OPTIONS = { + 'depth': 1, + 'minimum': -999999, + 'maximum': 999999, + 'dict_key_types': {str, int}, + 'csv_columns_minimum': 10, + 'csv_columns_maximum': 64, + 'csv_rows_minimum': 10, + 'csv_rows_maximum': 64, + } + +INPUT_TYPES = {'csv', 'json', 'yaml'} +OUTPUT_TYPES = {'csv', 'json', 'yaml'} + + +def test_convert_to_csv(): + for input_type in INPUT_TYPES: + input_stream = generate(input_type, { + **GENERATE_OPTIONS, + 'data_types': {bool,int,float,str}, + }) + + if isinstance(input_stream, str): + input_stream = StringIO(input_stream) + + + convert(input_stream, input_type, StringIO(), 'csv') + +def test_convert_to_json(): + for input_type in INPUT_TYPES: + input_stream = generate(input_type, GENERATE_OPTIONS) + + if isinstance(input_stream, str): + input_stream = StringIO(input_stream) + + convert(input_stream, input_type, StringIO(), 'json') + +def test_convert_to_yaml(): + for input_type in INPUT_TYPES: + input_stream = generate(input_type, GENERATE_OPTIONS) + + if isinstance(input_stream, str): + input_stream = StringIO(input_stream) + + convert(input_stream, input_type, StringIO(), 'yaml') + + +def test_convert_deep_json_to_yaml(): + input_stream = StringIO(generate('json', {**GENERATE_OPTIONS, 'depth': 4})) + convert(input_stream, 'json', StringIO(), 'yaml') + +def test_convert_deep_yaml_to_json(): + input_stream = generate('yaml', {**GENERATE_OPTIONS, 'depth': 4}) + convert(input_stream, 'yaml', StringIO(), 'json') + + +def test_convert_output_unsupported(): + for input_type in list(INPUT_TYPES): + with raises(ValueError): + convert(StringIO(), input_type, StringIO(), generate(str)) + +def test_convert_input_unsupported(): + for output_type in list(OUTPUT_TYPES): + with raises(ValueError): + convert(StringIO(), generate(str), StringIO(), output_type) diff --git a/py/lib/scwrypts/getenv.py b/py/lib/scwrypts/env.py similarity index 79% rename from py/lib/scwrypts/getenv.py rename to py/lib/scwrypts/env.py index ddc8da6..1003932 100644 --- a/py/lib/scwrypts/getenv.py +++ b/py/lib/scwrypts/env.py @@ -1,6 +1,6 @@ from os import getenv as os_getenv -from py.lib.scwrypts.exceptions import MissingVariableError +from .scwrypts.exceptions import MissingVariableError def getenv(name, required=True): diff --git a/py/lib/scwrypts/fzf/__init__.py b/py/lib/scwrypts/fzf/__init__.py new file mode 100644 index 0000000..e6e5a91 --- /dev/null +++ b/py/lib/scwrypts/fzf/__init__.py @@ -0,0 +1 @@ +from .client import fzf, fzf_tail, fzf_head diff --git a/py/lib/fzf/client.py b/py/lib/scwrypts/fzf/client.py similarity index 100% rename from py/lib/fzf/client.py rename to py/lib/scwrypts/fzf/client.py diff --git a/py/lib/scwrypts/http/__init__.py b/py/lib/scwrypts/http/__init__.py new file mode 100644 index 0000000..bb5f65d --- /dev/null +++ b/py/lib/scwrypts/http/__init__.py @@ -0,0 +1 @@ +from .client import get_request_client diff --git a/py/lib/http/client.py b/py/lib/scwrypts/http/client.py similarity index 100% rename from py/lib/http/client.py rename to py/lib/scwrypts/http/client.py diff --git a/py/lib/http/directus/constant.py b/py/lib/scwrypts/http/directus/__init__.py similarity index 95% rename from py/lib/http/directus/constant.py rename to py/lib/scwrypts/http/directus/__init__.py index 5f3b294..762e5eb 100644 --- a/py/lib/http/directus/constant.py +++ b/py/lib/scwrypts/http/directus/__init__.py @@ -1,3 +1,5 @@ +from .client import * + FILTER_OPERATORS = { '_eq', '_neq', diff --git a/py/lib/http/directus/client.py b/py/lib/scwrypts/http/directus/client.py similarity index 94% rename from py/lib/http/directus/client.py rename to py/lib/scwrypts/http/directus/client.py index 07e5e00..6798dbb 100644 --- a/py/lib/http/directus/client.py +++ b/py/lib/scwrypts/http/directus/client.py @@ -1,5 +1,6 @@ -from py.lib.http import get_request_client -from py.lib.scwrypts import getenv +from scwrypts.env import getenv + +from .. import get_request_client REQUEST = None diff --git a/py/lib/scwrypts/http/discord/__init__.py b/py/lib/scwrypts/http/discord/__init__.py new file mode 100644 index 0000000..f4dcc0b --- /dev/null +++ b/py/lib/scwrypts/http/discord/__init__.py @@ -0,0 +1,2 @@ +from .client import * +from .send_message import * diff --git a/py/lib/http/discord/client.py b/py/lib/scwrypts/http/discord/client.py similarity index 86% rename from py/lib/http/discord/client.py rename to py/lib/scwrypts/http/discord/client.py index 36ba4da..2ddedec 100644 --- a/py/lib/http/discord/client.py +++ b/py/lib/scwrypts/http/discord/client.py @@ -1,5 +1,5 @@ -from py.lib.http import get_request_client -from py.lib.scwrypts import getenv +from scwrypts.env import getenv +from scwrypts.http import get_request_client REQUEST = None diff --git a/py/lib/http/discord/send_message.py b/py/lib/scwrypts/http/discord/send_message.py similarity index 95% rename from py/lib/http/discord/send_message.py rename to py/lib/scwrypts/http/discord/send_message.py index 6385a8d..627b991 100644 --- a/py/lib/http/discord/send_message.py +++ b/py/lib/scwrypts/http/discord/send_message.py @@ -1,5 +1,5 @@ -from py.lib.scwrypts import getenv -from py.lib.http.discord import request +from scwrypts.env import getenv +from .client import request def send_message(content, channel_id=None, webhook=None, username=None, avatar_url=None, **kwargs): if username is None: diff --git a/py/lib/scwrypts/http/linear/__init__.py b/py/lib/scwrypts/http/linear/__init__.py new file mode 100644 index 0000000..421945b --- /dev/null +++ b/py/lib/scwrypts/http/linear/__init__.py @@ -0,0 +1 @@ +from .client import * diff --git a/py/lib/http/linear/client.py b/py/lib/scwrypts/http/linear/client.py similarity index 86% rename from py/lib/http/linear/client.py rename to py/lib/scwrypts/http/linear/client.py index 63be5d9..9fe97e4 100644 --- a/py/lib/http/linear/client.py +++ b/py/lib/scwrypts/http/linear/client.py @@ -1,5 +1,6 @@ -from py.lib.http import get_request_client -from py.lib.scwrypts import getenv +from scwrypts.env import getenv + +from .. import get_request_client REQUEST = None diff --git a/py/lib/scwrypts/io/__init__.py b/py/lib/scwrypts/io/__init__.py new file mode 100644 index 0000000..4f6543c --- /dev/null +++ b/py/lib/scwrypts/io/__init__.py @@ -0,0 +1 @@ +from .combined_io_stream import get_combined_stream, add_io_arguments diff --git a/py/lib/scwrypts/io.py b/py/lib/scwrypts/io/combined_io_stream.py similarity index 94% rename from py/lib/scwrypts/io.py rename to py/lib/scwrypts/io/combined_io_stream.py index eed5a60..8c81687 100644 --- a/py/lib/scwrypts/io.py +++ b/py/lib/scwrypts/io/combined_io_stream.py @@ -2,7 +2,7 @@ from contextlib import contextmanager from pathlib import Path from sys import stdin, stdout, stderr -from py.lib.scwrypts.getenv import getenv +from scwrypts.env import getenv @contextmanager @@ -34,8 +34,8 @@ def get_stream(filename=None, mode='r', encoding='utf-8', verbose=False, **kwarg stdout.flush() -def add_io_arguments(parser, toggle_input=True, toggle_output=True): - if toggle_input: +def add_io_arguments(parser, allow_input=True, allow_output=True): + if allow_input: parser.add_argument( '-i', '--input-file', dest = 'input_file', @@ -44,7 +44,7 @@ def add_io_arguments(parser, toggle_input=True, toggle_output=True): required = False, ) - if toggle_output: + if allow_output: parser.add_argument( '-o', '--output-file', dest = 'output_file', diff --git a/py/lib/scwrypts/redis/__init__.py b/py/lib/scwrypts/redis/__init__.py new file mode 100644 index 0000000..88d6f63 --- /dev/null +++ b/py/lib/scwrypts/redis/__init__.py @@ -0,0 +1 @@ +from .client import get_client diff --git a/py/lib/redis/client.py b/py/lib/scwrypts/redis/client.py similarity index 92% rename from py/lib/redis/client.py rename to py/lib/scwrypts/redis/client.py index 22d3827..76396d2 100644 --- a/py/lib/redis/client.py +++ b/py/lib/scwrypts/redis/client.py @@ -1,6 +1,6 @@ from redis import StrictRedis -from py.lib.scwrypts import getenv +from scwrypts.env import getenv CLIENT = None diff --git a/py/lib/scwrypts/run.py b/py/lib/scwrypts/run.py deleted file mode 100644 index f0a607e..0000000 --- a/py/lib/scwrypts/run.py +++ /dev/null @@ -1,22 +0,0 @@ -from os import getenv -from pathlib import Path -from subprocess import run as subprocess_run - - -def run(scwrypt_name, *args): - DEPTH = int(getenv('SUBSCWRYPT', '0')) - DEPTH += 1 - - SCWRYPTS_EXE = Path(__file__).parents[3] / 'scwrypts' - ARGS = ' '.join([str(x) for x in args]) - print(f'SUBSCWRYPT={DEPTH} {SCWRYPTS_EXE} {scwrypt_name} -- {ARGS}') - - print(f'\n {"--"*DEPTH} ({DEPTH}) BEGIN SUBSCWRYPT : {Path(scwrypt_name).name}') - subprocess_run( - f'SUBSCWRYPT={DEPTH} {SCWRYPTS_EXE} {scwrypt_name} -- {ARGS}', - shell=True, - executable='/bin/zsh', - check=False, - ) - - print(f' {"--"*DEPTH} ({DEPTH}) END SUBSCWRYPT : {Path(scwrypt_name).name}\n') diff --git a/py/lib/scwrypts/scwrypts/__init__.py b/py/lib/scwrypts/scwrypts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/py/lib/scwrypts/exceptions.py b/py/lib/scwrypts/scwrypts/exceptions.py similarity index 75% rename from py/lib/scwrypts/exceptions.py rename to py/lib/scwrypts/scwrypts/exceptions.py index 367ea50..2eb90e2 100644 --- a/py/lib/scwrypts/exceptions.py +++ b/py/lib/scwrypts/scwrypts/exceptions.py @@ -6,11 +6,11 @@ class MissingVariableError(EnvironmentError): super().__init__(f'Missing required environment variable "{name}"') -class ImportedExecutableError(ImportError): - def __init__(self): - super().__init__('executable only; must run through scwrypts') - - class MissingFlagAndEnvironmentVariableError(EnvironmentError, ArgumentError): def __init__(self, flags, env_var): super().__init__(f'must provide at least one of : {{ flags: {flags} OR {env_var} }}') + + +class MissingScwryptsExecutableError(EnvironmentError): + def __init__(self): + super().__init__(f'scwrypts must be installed and available on your PATH') diff --git a/py/lib/scwrypts/execute.py b/py/lib/scwrypts/scwrypts/execute.py similarity index 64% rename from py/lib/scwrypts/execute.py rename to py/lib/scwrypts/scwrypts/execute.py index e85feea..85e3b32 100644 --- a/py/lib/scwrypts/execute.py +++ b/py/lib/scwrypts/scwrypts/execute.py @@ -1,9 +1,12 @@ from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter -from py.lib.scwrypts.io import get_combined_stream, add_io_arguments +from scwrypts.io import get_combined_stream, add_io_arguments -def execute(main, description=None, parse_args=None, toggle_input=True, toggle_output=True): +def execute(main, description=None, parse_args=None, allow_input=True, allow_output=True): + ''' + API to initiate a python-based scwrypt + ''' if parse_args is None: parse_args = [] @@ -12,7 +15,7 @@ def execute(main, description=None, parse_args=None, toggle_input=True, toggle_o formatter_class = ArgumentDefaultsHelpFormatter, ) - add_io_arguments(parser, toggle_input, toggle_output) + add_io_arguments(parser, allow_input, allow_output) for a in parse_args: parser.add_argument(*a[0], **a[1]) diff --git a/py/lib/scwrypts/interactive.py b/py/lib/scwrypts/scwrypts/interactive.py similarity index 83% rename from py/lib/scwrypts/interactive.py rename to py/lib/scwrypts/scwrypts/interactive.py index b8f477f..266c843 100644 --- a/py/lib/scwrypts/interactive.py +++ b/py/lib/scwrypts/scwrypts/interactive.py @@ -2,6 +2,9 @@ from bpython import embed def interactive(variable_descriptions): + ''' + main() decorator to drop to interactive python environment upon completion + ''' def outer(function): def inner(*args, **kwargs): diff --git a/py/lib/scwrypts/scwrypts/scwrypts.py b/py/lib/scwrypts/scwrypts/scwrypts.py new file mode 100644 index 0000000..5367d8a --- /dev/null +++ b/py/lib/scwrypts/scwrypts/scwrypts.py @@ -0,0 +1,30 @@ +from os import getenv +from shutil import which +from subprocess import run + +from .exceptions import MissingScwryptsExecutableError + + +def scwrypts(name, group, _type, *args, log_level=None): + ''' + invoke non-python scwrypts from python + ''' + executable = which('scwrypts') + if executable is None: + raise MissingScwryptsExecutableError() + + pre_args = '' + + if log_level is not None: + pre_args += '--log-level {log_level}' + + depth = getenv('SUBSCWRYPT', '') + if depth != '': + depth = int(depth) + 1 + + return run( + f'SUBSCWRYPT={depth} {executable} --name {name} --group {group} --type {_type} {pre_args} -- {" ".join(args)}', + shell=True, + executable='/bin/zsh', + check=False, + ) diff --git a/py/lib/scwrypts/test/__init__.py b/py/lib/scwrypts/test/__init__.py new file mode 100644 index 0000000..4448ae4 --- /dev/null +++ b/py/lib/scwrypts/test/__init__.py @@ -0,0 +1 @@ +from .random_generator import generate diff --git a/py/lib/scwrypts/test/exceptions.py b/py/lib/scwrypts/test/exceptions.py new file mode 100644 index 0000000..1491f0d --- /dev/null +++ b/py/lib/scwrypts/test/exceptions.py @@ -0,0 +1,10 @@ +class GeneratorError(Exception): + pass + +class NoDataTypeError(GeneratorError, ValueError): + def __init__(self): + super().__init__('must provide at least one data type (either "data_type" or "data_types")') + +class BadGeneratorTypeError(GeneratorError, ValueError): + def __init__(self, data_type): + super().__init__(f'no generator exists for data type "{data_type}"') diff --git a/py/lib/scwrypts/test/random_generator.py b/py/lib/scwrypts/test/random_generator.py new file mode 100644 index 0000000..91f6499 --- /dev/null +++ b/py/lib/scwrypts/test/random_generator.py @@ -0,0 +1,240 @@ +from csv import writer, QUOTE_NONNUMERIC +from io import StringIO +from json import dumps +from random import randint, uniform, choice +from re import sub +from string import printable +from yaml import safe_dump + +from .exceptions import NoDataTypeError, BadGeneratorTypeError + + +SUPPORTED_DATA_TYPES = None + +DEFAULT_OPTIONS = { + 'data_types': None, + 'minimum': 0, + 'maximum': 64, + 'depth': 1, + 'character_set': None, + 'bool_nullable': False, + 'str_length': None, + 'str_minimum_length': 0, + 'str_maximum_length': 32, + 'list_length': 8, + 'set_length': 8, + 'dict_length': 8, + 'dict_key_types': {int, float, chr, str}, + 'csv_columns': None, + 'csv_columns_minimum': 1, + 'csv_columns_maximum': 16, + 'csv_rows': None, + 'csv_rows_minimum': 2, + 'csv_rows_maximum': 16, + 'json_initial_type': dict, + 'yaml_initial_type': dict, + } + +def generate(data_type=None, options=None): + ''' + generate random data with the call of a function + use data_type to generate a single value + + use options to set generation options (key = type, value = kwargs) + + use options.data_types and omit data_type to generate a random type + ''' + if options is None: + options = {**DEFAULT_OPTIONS} + else: + options = DEFAULT_OPTIONS | options + + if data_type is None and options['data_types'] is None: + raise NoDataTypeError() + + if data_type is None and options['data_types'] is not None: + return generate(data_type=choice(list(options['data_types'])), options=options) + + if not isinstance(data_type, str): + data_type = data_type.__name__ + + if data_type not in Generator.get_supported_data_types(): + raise BadGeneratorTypeError(data_type) + + return getattr(Generator, f'_{data_type}')(options) + +##################################################################### + +class Generator: + + @classmethod + def get_supported_data_types(cls): + global SUPPORTED_DATA_TYPES # pylint: disable=global-statement + if SUPPORTED_DATA_TYPES is None: + SUPPORTED_DATA_TYPES = { + sub('^_', '', data_type) + for data_type, method in Generator.__dict__.items() + if isinstance(method, staticmethod) + } + + return SUPPORTED_DATA_TYPES + + + + @staticmethod + def _bool(options): + return choice([True, False, None]) if options['bool_nullable'] else choice([True, False]) + + + @staticmethod + def _int(options): + return randint(options['minimum'], options['maximum']) + + + @staticmethod + def _float(options): + return uniform(options['minimum'], options['maximum']) + + + @staticmethod + def _chr(options): + character_set = options['character_set'] + return choice(character_set) if character_set is not None else chr(randint(0,65536)) + + + @staticmethod + def _str(options): + length = options['str_length'] + if length is None: + length = generate(int, { + 'minimum': options['str_minimum_length'], + 'maximum': options['str_maximum_length'], + }) + return ''.join((generate(chr, options) for _ in range(length))) + + + @staticmethod + def _list(options): + if options['depth'] <= 0: + return [] + + options['depth'] -= 1 + + if options['data_types'] is None: + options['data_types'] = {bool, int, float, chr, str} + + return [ generate(None, options) for _ in range(options['list_length']) ] + + + @staticmethod + def _set(options): + if options['depth'] <= 0: + return set() + + options['depth'] -= 1 + + if options['data_types'] is None: + options['data_types'] = {bool, int, float, chr, str} + + set_options = options | {'data_types': options['data_types'] - {list, dict, set}} + + return { generate(None, set_options) for _ in range(options['set_length']) } + + + @staticmethod + def _dict(options): + if options['depth'] <= 0: + return {} + + options['depth'] -= 1 + + if options['data_types'] is None: + options['data_types'] = {bool, int, float, chr, str, list, set, dict} + + if len(options['data_types']) == 0: + return {} + + key_options = options | {'data_types': options['dict_key_types']} + return { + generate(None, key_options): generate(None, options) + for _ in range(options['dict_length']) + } + + + @staticmethod + def _csv(options): + ''' + creates a StringIO object containing csv data + ''' + if options['data_types'] is None: + options['data_types'] = {int, float, str} + + columns = options['csv_columns'] + if columns is None: + columns = max(1, generate(int, { + 'minimum': options['csv_columns_minimum'], + 'maximum': options['csv_columns_maximum'], + })) + + rows = options['csv_rows'] + if rows is None: + rows = max(1, generate(int, { + 'minimum': options['csv_rows_minimum'], + 'maximum': options['csv_rows_maximum'], + })) + + if options['character_set'] is None: + options['character_set'] = printable + + csv = StringIO() + csv_writer = writer(csv, quoting=QUOTE_NONNUMERIC) + + options['list_length'] = columns + + for line in [ generate(list, options) for _ in range(rows) ]: + csv_writer.writerow(line) + + csv.seek(0) + return csv + + + @staticmethod + def _json(options): + ''' + creates a str containing json data + ''' + if options['data_types'] is None: + options['data_types'] = {bool, int, float, str, list, dict} + + if options['character_set'] is None: + options['character_set'] = printable + + options['dict_key_types'] = { int, float, str } + + data = generate(options['json_initial_type'], options) + return dumps(data) + + + @staticmethod + def _yaml(options): + ''' + creates a StringIO object containing yaml data + ''' + if options['data_types'] is None: + options['data_types'] = {bool, int, float, str, list, dict} + + if options['character_set'] is None: + options['character_set'] = printable + + options['dict_key_types'] = { int, float, str } + + yaml = StringIO() + safe_dump(generate(options['yaml_initial_type'], options), yaml, default_flow_style=False) + + yaml.seek(0) + return yaml + + +##################################################################### +if __name__ == '__main__': + print(generate('json', {'depth': 3})) diff --git a/py/lib/scwrypts/test/test_random_generator.py b/py/lib/scwrypts/test/test_random_generator.py new file mode 100644 index 0000000..27148b4 --- /dev/null +++ b/py/lib/scwrypts/test/test_random_generator.py @@ -0,0 +1,44 @@ +from os import getenv +from random import randint + +from .random_generator import generate, Generator + + +ITERATIONS = int(getenv('PYTEST_ITERATIONS__scwrypts__test__random_generator', getenv('PYTEST_ITERATIONS', '999'))) + + +def test_generate(): # generators should be quick and "just work" (no Exceptions) + for data_type in Generator.get_supported_data_types(): + for _ in range(ITERATIONS): + generate(data_type) + + +def test_generate_depth_deep(): + for data_type in Generator.get_supported_data_types(): + generate(data_type, {'depth': 4}) + +def test_generate_depth_shallow(): + for data_type in Generator.get_supported_data_types(): + generate(data_type, {'depth': randint(-999, 0)}) + + +def test_generate_range_all(): + for data_type in Generator.get_supported_data_types(): + generate(data_type, {'minimum': -99, 'maximum': 99}) + +def test_generate_range_positive(): + for data_type in Generator.get_supported_data_types(): + generate(data_type, {'minimum': 1, 'maximum': 99}) + +def test_generate_range_zero(): + for data_type in Generator.get_supported_data_types(): + generate(data_type, {'minimum': 3, 'maximum': 3}) + +def test_generate_range_negative(): + for data_type in Generator.get_supported_data_types(): + generate(data_type, {'minimum': -99, 'maximum': -1}) + + +def test_generate_bool_nullable(): + for data_type in Generator.get_supported_data_types(): + generate(data_type, {'bool': {'nullable': True}}) diff --git a/py/lib/scwrypts/twilio/__init__.py b/py/lib/scwrypts/twilio/__init__.py new file mode 100644 index 0000000..7af62da --- /dev/null +++ b/py/lib/scwrypts/twilio/__init__.py @@ -0,0 +1,2 @@ +from .client import get_client +from .send_sms import send_sms diff --git a/py/lib/twilio/client.py b/py/lib/scwrypts/twilio/client.py similarity index 92% rename from py/lib/twilio/client.py rename to py/lib/scwrypts/twilio/client.py index 20c1a84..5e40de6 100644 --- a/py/lib/twilio/client.py +++ b/py/lib/scwrypts/twilio/client.py @@ -1,6 +1,6 @@ from twilio.rest import Client -from py.lib.scwrypts import getenv +from scwrypts.env import getenv CLIENT = None diff --git a/py/lib/twilio/send_sms.py b/py/lib/scwrypts/twilio/send_sms.py similarity index 97% rename from py/lib/twilio/send_sms.py rename to py/lib/scwrypts/twilio/send_sms.py index 752ebac..bb79414 100644 --- a/py/lib/twilio/send_sms.py +++ b/py/lib/scwrypts/twilio/send_sms.py @@ -1,7 +1,7 @@ from json import dumps from time import sleep -from py.lib.twilio.client import get_client +from .client import get_client def send_sms(to, from_, body, max_char_count=300, stream=None): diff --git a/py/lib/twilio/__init__.py b/py/lib/twilio/__init__.py deleted file mode 100644 index 2803123..0000000 --- a/py/lib/twilio/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from py.lib.twilio.client import get_client -from py.lib.twilio.send_sms import send_sms diff --git a/py/linear/comment.py b/py/linear/comment.py index daf4a50..6d7b82d 100755 --- a/py/linear/comment.py +++ b/py/linear/comment.py @@ -1,13 +1,22 @@ #!/usr/bin/env python -from py.lib.http.linear import graphql -from py.lib.scwrypts import execute - -from py.lib.scwrypts.exceptions import ImportedExecutableError - -if __name__ != '__main__': - raise ImportedExecutableError() - +from scwrypts import execute ##################################################################### +from scwrypts.http.linear import graphql + + +description = 'comment on an issue in linear.app' +parse_args = [ + ( ['-d', '--issue-id'], { + 'dest' : 'issue_id', + 'help' : 'issue short-code (e.g. CLOUD-319)', + 'required' : True, + }), + ( ['-m', '--message'], { + 'dest' : 'message', + 'help' : 'comment to post to the target issue', + 'required' : True, + }), + ] def get_query(args): @@ -26,20 +35,6 @@ def main(args, stream): response = graphql(get_query(args)) stream.writeline(response) - ##################################################################### -execute(main, - description = 'comment on an inssue in linear.app', - parse_args = [ - ( ['-d', '--issue-id'], { - 'dest' : 'issue_id', - 'help' : 'issue short-code (e.g. CLOUD-319)', - 'required' : True, - }), - ( ['-m', '--message'], { - 'dest' : 'message', - 'help' : 'comment to post to the target issue', - 'required' : True, - }), - ] - ) +if __name__ == '__main__': + execute(main, description, parse_args) diff --git a/py/redis/interactive.py b/py/redis/interactive.py index d018039..c0e22a2 100755 --- a/py/redis/interactive.py +++ b/py/redis/interactive.py @@ -1,15 +1,14 @@ #!/usr/bin/env python -from py.lib.redis import get_client -from py.lib.scwrypts import execute, interactive, getenv - -from py.lib.scwrypts.exceptions import ImportedExecutableError - -if __name__ != '__main__': - raise ImportedExecutableError() - +from scwrypts import execute ##################################################################### +from scwrypts import interactive +from scwrypts.env import getenv +from scwrypts.redis import get_client +description = 'establishes a redis client in an interactive python shell' +parse_args = [] + @interactive([ f'r = StrictRedis(\'{getenv("REDIS_HOST")}:{getenv("REDIS_PORT")}\')', ]) @@ -20,7 +19,5 @@ def main(_args, _stream): ##################################################################### -execute(main, - description = 'establishes a redis client in an interactive python shell', - parse_args = [], - ) +if __name__ == '__main__': + execute(main, description, parse_args) diff --git a/py/requirements.txt b/py/requirements.txt index 68453d8..efdb03a 100644 --- a/py/requirements.txt +++ b/py/requirements.txt @@ -1,5 +1 @@ -bpython -pyfzf -pyyaml -redis -twilio +./lib diff --git a/py/twilio/send-sms.py b/py/twilio/send-sms.py index c089751..a08a0ae 100755 --- a/py/twilio/send-sms.py +++ b/py/twilio/send-sms.py @@ -1,15 +1,38 @@ #!/usr/bin/env python +from scwrypts import execute +##################################################################### from sys import stderr -from py.lib.scwrypts import execute, getenv -from py.lib.twilio import send_sms +from scwrypts.env import getenv +from scwrypts.twilio import send_sms -from py.lib.scwrypts.exceptions import ImportedExecutableError, MissingFlagAndEnvironmentVariableError -if __name__ != '__main__': - raise ImportedExecutableError() - -##################################################################### +description = 'send a simple SMS through twilio' +parse_args = [ + ( ['-t', '--to'], { + 'dest' : 'to', + 'help' : 'phone number of the receipient', + 'required' : False, + 'default' : getenv('TWILIO__DEFAULT_PHONE_TO', required=False), + }), + ( ['-f', '--from'], { + 'dest' : 'from_', + 'help' : 'phone number of the receipient', + 'required' : False, + 'default' : getenv('TWILIO__DEFAULT_PHONE_FROM', required=False), + }), + ( ['-b', '--body'], { + 'dest' : 'body', + 'help' : 'message body', + 'required' : False, + }), + ( ['--max-char-count'], { + 'dest' : 'max_char_count', + 'help' : 'separate message into parts by character count (1 < N <= 1500)', + 'required' : False, + 'default' : 300, + }), + ] def main(args, stream): if args.body is None: @@ -35,31 +58,5 @@ def main(args, stream): ##################################################################### -execute(main, - description = 'send a simple SMS through twilio', - parse_args = [ - ( ['-t', '--to'], { - 'dest' : 'to', - 'help' : 'phone number of the receipient', - 'required' : False, - 'default' : getenv('TWILIO__DEFAULT_PHONE_TO', required=False), - }), - ( ['-f', '--from'], { - 'dest' : 'from_', - 'help' : 'phone number of the receipient', - 'required' : False, - 'default' : getenv('TWILIO__DEFAULT_PHONE_FROM', required=False), - }), - ( ['-b', '--body'], { - 'dest' : 'body', - 'help' : 'message body', - 'required' : False, - }), - ( ['--max-char-count'], { - 'dest' : 'max_char_count', - 'help' : 'separate message into parts by character count (1 < N <= 1500)', - 'required' : False, - 'default' : 300, - }), - ] - ) +if __name__ == '__main__': + execute(main, description, parse_args)