refactor py/lib into python-scwrypts subproject
This commit is contained in:
parent
d4ef1c70e0
commit
3bcd4f3f6d
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
1
py/lib/.gitignore
vendored
Normal file
1
py/lib/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
dist/
|
3
py/lib/README.md
Normal file
3
py/lib/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Python Scwrypts
|
||||
[![Generic Badge](https://img.shields.io/badge/python->=3.9-informational.svg)](https://python.org)
|
||||
<br>
|
@ -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
|
@ -1 +0,0 @@
|
||||
import py.lib.data.converter
|
@ -1 +0,0 @@
|
||||
from py.lib.fzf.client import fzf, fzf_tail, fzf_head
|
@ -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
|
@ -1,2 +0,0 @@
|
||||
from py.lib.http.directus.client import *
|
||||
from py.lib.http.directus.constant import *
|
@ -1,2 +0,0 @@
|
||||
from py.lib.http.discord.client import *
|
||||
from py.lib.http.discord.send_message import *
|
@ -1 +0,0 @@
|
||||
from py.lib.http.linear.client import *
|
61
py/lib/pyproject.toml
Normal file
61
py/lib/pyproject.toml
Normal file
@ -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*']
|
||||
|
@ -1 +0,0 @@
|
||||
from py.lib.redis.client import get_client
|
@ -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
|
||||
|
1
py/lib/scwrypts/data/__init__.py
Normal file
1
py/lib/scwrypts/data/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .converter import convert
|
@ -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)
|
||||
|
75
py/lib/scwrypts/data/test_converter.py
Normal file
75
py/lib/scwrypts/data/test_converter.py
Normal file
@ -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)
|
@ -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):
|
1
py/lib/scwrypts/fzf/__init__.py
Normal file
1
py/lib/scwrypts/fzf/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .client import fzf, fzf_tail, fzf_head
|
1
py/lib/scwrypts/http/__init__.py
Normal file
1
py/lib/scwrypts/http/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .client import get_request_client
|
@ -1,3 +1,5 @@
|
||||
from .client import *
|
||||
|
||||
FILTER_OPERATORS = {
|
||||
'_eq',
|
||||
'_neq',
|
@ -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
|
2
py/lib/scwrypts/http/discord/__init__.py
Normal file
2
py/lib/scwrypts/http/discord/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from .client import *
|
||||
from .send_message import *
|
@ -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
|
||||
|
@ -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:
|
1
py/lib/scwrypts/http/linear/__init__.py
Normal file
1
py/lib/scwrypts/http/linear/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .client import *
|
@ -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
|
||||
|
1
py/lib/scwrypts/io/__init__.py
Normal file
1
py/lib/scwrypts/io/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .combined_io_stream import get_combined_stream, add_io_arguments
|
@ -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',
|
1
py/lib/scwrypts/redis/__init__.py
Normal file
1
py/lib/scwrypts/redis/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .client import get_client
|
@ -1,6 +1,6 @@
|
||||
from redis import StrictRedis
|
||||
|
||||
from py.lib.scwrypts import getenv
|
||||
from scwrypts.env import getenv
|
||||
|
||||
CLIENT = None
|
||||
|
@ -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')
|
0
py/lib/scwrypts/scwrypts/__init__.py
Normal file
0
py/lib/scwrypts/scwrypts/__init__.py
Normal file
@ -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')
|
@ -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])
|
@ -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):
|
30
py/lib/scwrypts/scwrypts/scwrypts.py
Normal file
30
py/lib/scwrypts/scwrypts/scwrypts.py
Normal file
@ -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,
|
||||
)
|
1
py/lib/scwrypts/test/__init__.py
Normal file
1
py/lib/scwrypts/test/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .random_generator import generate
|
10
py/lib/scwrypts/test/exceptions.py
Normal file
10
py/lib/scwrypts/test/exceptions.py
Normal file
@ -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}"')
|
240
py/lib/scwrypts/test/random_generator.py
Normal file
240
py/lib/scwrypts/test/random_generator.py
Normal file
@ -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}))
|
44
py/lib/scwrypts/test/test_random_generator.py
Normal file
44
py/lib/scwrypts/test/test_random_generator.py
Normal file
@ -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}})
|
2
py/lib/scwrypts/twilio/__init__.py
Normal file
2
py/lib/scwrypts/twilio/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from .client import get_client
|
||||
from .send_sms import send_sms
|
@ -1,6 +1,6 @@
|
||||
from twilio.rest import Client
|
||||
|
||||
from py.lib.scwrypts import getenv
|
||||
from scwrypts.env import getenv
|
||||
|
||||
CLIENT = None
|
||||
|
@ -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):
|
@ -1,2 +0,0 @@
|
||||
from py.lib.twilio.client import get_client
|
||||
from py.lib.twilio.send_sms import send_sms
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -1,5 +1 @@
|
||||
bpython
|
||||
pyfzf
|
||||
pyyaml
|
||||
redis
|
||||
twilio
|
||||
./lib
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user