kkpy-cli 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. kkpy/__init__.py +0 -0
  2. kkpy/__version__.py +1 -0
  3. kkpy/poetry/install-poetry.py +831 -0
  4. kkpy/poetry/install.sh +22 -0
  5. kkpy/template.py +227 -0
  6. kkpy/templates/.editorconfig +23 -0
  7. kkpy/templates/.gitignore +54 -0
  8. kkpy/templates/.requirements-template.txt +4 -0
  9. kkpy/templates/.requirements-test.txt +3 -0
  10. kkpy/templates/Jenkinsfile +0 -0
  11. kkpy/templates/README.md +0 -0
  12. kkpy/templates/bin/start.py +33 -0
  13. kkpy/templates/bin/start.sh +9 -0
  14. kkpy/templates/src/__init__.py +11 -0
  15. kkpy/templates/src/configs/app.ini +9 -0
  16. kkpy/templates/src/configs/db.ini +5 -0
  17. kkpy/templates/src/configs/logging_config.ini +27 -0
  18. kkpy/templates/src/main.py +27 -0
  19. kkpy/templates/src/requirements.txt +0 -0
  20. kkpy/templates/src/utils/__init__.py +0 -0
  21. kkpy/templates/src/utils/utils.py +63 -0
  22. kkpy/templates/tests/__init__.py +0 -0
  23. kkpy/templates/tests/conftest.py +9 -0
  24. kkpy/templates/tests/test_sample.py +4 -0
  25. kkpy/templates/tox.ini +27 -0
  26. kkpy_cli/__init__.py +0 -0
  27. kkpy_cli/cli.py +197 -0
  28. kkpy_cli/commands/__init__.py +0 -0
  29. kkpy_cli/commands/build.py +34 -0
  30. kkpy_cli/commands/clean.py +19 -0
  31. kkpy_cli/commands/init.py +34 -0
  32. kkpy_cli/commands/install.py +22 -0
  33. kkpy_cli/commands/run.py +23 -0
  34. kkpy_cli/commands/test.py +15 -0
  35. kkpy_cli/utils/__init__.py +1 -0
  36. kkpy_cli/utils/cli_args.py +21 -0
  37. kkpy_cli/utils/exception.py +12 -0
  38. kkpy_cli-0.1.1.dist-info/METADATA +26 -0
  39. kkpy_cli-0.1.1.dist-info/RECORD +42 -0
  40. kkpy_cli-0.1.1.dist-info/WHEEL +5 -0
  41. kkpy_cli-0.1.1.dist-info/entry_points.txt +2 -0
  42. kkpy_cli-0.1.1.dist-info/top_level.txt +2 -0
kkpy/poetry/install.sh ADDED
@@ -0,0 +1,22 @@
1
+ # !/bin/bash
2
+ export HRER="$(cd "`dirname "$0"`"; pwd)"
3
+
4
+ echo "========================= [Install Poetry] ========================="
5
+ INPUT_POETRY_VERSION=$1
6
+ INPUT_POETRY_HOME=$2
7
+
8
+ echo "> Install poetry(v${INPUT_POETRY_VERSION})."
9
+ echo "> Located at: ${INPUT_POETRY_HOME}"
10
+ export POETRY_HOME=${INPUT_POETRY_HOME}
11
+
12
+ python3 ${HRER}/install-poetry.py --version ${INPUT_POETRY_VERSION}
13
+
14
+ # check execution result
15
+ if [ $? -eq 0 ] ;then
16
+ echo "> Successfully."
17
+ else
18
+ echo "> Failed."
19
+ exit 1
20
+ fi
21
+
22
+ echo "===================================================================="
kkpy/template.py ADDED
@@ -0,0 +1,227 @@
1
+ import distutils
2
+ import logging
3
+ import os
4
+ import platform
5
+ import subprocess
6
+ import uuid
7
+ from distutils import dir_util
8
+
9
+ here = os.path.abspath(os.path.dirname(__file__))
10
+
11
+ TEMPLATE_VENV_NAME = '.venv'
12
+
13
+
14
+ class Template:
15
+
16
+ LOG_DIR = 'logs'
17
+
18
+ def __init__(self, template_home):
19
+ self.template_home = template_home
20
+ self._init_basic_folder()
21
+
22
+ def _init_basic_folder(self):
23
+ # log directory
24
+ log_dir = os.path.join(self.template_home, self.LOG_DIR)
25
+ if not os.path.exists(log_dir):
26
+ logging.info(f"make new directory: {log_dir}")
27
+ os.makedirs(log_dir, exist_ok=True)
28
+
29
+ def generate(self):
30
+ logging.info(f"start to copy template files from template to {self.template_home}")
31
+ distutils.dir_util.copy_tree(f'{here}/templates', f"{self.template_home}")
32
+
33
+
34
+ class TemplateEnv:
35
+
36
+ TEMPLATE_ENV_NAME = '.env'
37
+
38
+ def __init__(self, template_home):
39
+ self.template_home = template_home
40
+
41
+ @property
42
+ def project_home(self):
43
+ return self.template_home
44
+
45
+ @property
46
+ def project_name(self):
47
+ return os.path.basename(self.project_home)
48
+
49
+ @property
50
+ def env_path(self):
51
+ return os.path.join(self.template_home, self.TEMPLATE_ENV_NAME)
52
+
53
+ @property
54
+ def venv_conf_path(self):
55
+ return os.path.join(self.template_home, TEMPLATE_VENV_NAME)
56
+
57
+ @property
58
+ def env(self):
59
+ return self._env
60
+
61
+ @env.setter
62
+ def env(self, value):
63
+ self._env = value
64
+
65
+ @property
66
+ def py_ver(self):
67
+ return self._py_ver
68
+
69
+ @py_ver.setter
70
+ def py_ver(self, value):
71
+ self._py_ver = value
72
+
73
+ @property
74
+ def venv_path(self):
75
+ return self._venv_path
76
+
77
+ @venv_path.setter
78
+ def venv_path(self, value):
79
+ self._venv_path = value
80
+
81
+ @staticmethod
82
+ def check_is_exist(env_directory):
83
+ if os.path.isfile(os.path.join(env_directory, TemplateEnv.TEMPLATE_ENV_NAME)):
84
+ return True
85
+
86
+ return False
87
+
88
+ def create(self):
89
+ with open(self.env_path, 'w') as text_file:
90
+ text_file.write('# It is automatically generated at init time, please do not edit it. \n')
91
+
92
+ logging.info(f'write args in {self.env_path}')
93
+
94
+ logging.info(f'write {self.project_home} to arg: KKPY_PROJECT_HOME.')
95
+ text_file.write(f'KKPY_PROJECT_HOME=\"{self.project_home}\"\n')
96
+
97
+ logging.info(f'write {self.project_name} to arg: KKPY_PROJECT_NAME.')
98
+ text_file.write(f'KKPY_PROJECT_NAME=\"{self.project_name}\"\n')
99
+
100
+ logging.info(f'write {self.env_path} to arg: KKPY_ENV_PATH.')
101
+ text_file.write(f'KKPY_ENV_PATH=\"{self.env_path}\"\n')
102
+
103
+ logging.info(f'write {self.env} to arg: KKPY_ENV.')
104
+ text_file.write(f'KKPY_ENV=\"{self.env}\"\n')
105
+
106
+ logging.info(f'write {self.py_ver} to arg: KKPY_PYTHON_VERSION.')
107
+ text_file.write(f'KKPY_PYTHON_VERSION=\"{self.py_ver}\"\n')
108
+
109
+ logging.info(f'write {self.venv_conf_path} to arg: KKPY_VIRTUALENV_CONF_PATH.')
110
+ text_file.write(f'KKPY_VIRTUALENV_CONF_PATH=\"{self.venv_conf_path}\"\n')
111
+
112
+ logging.info(f'write {self.venv_path} to arg: KKPY_VIRTUALENV_PATH.')
113
+ text_file.write(f'KKPY_VIRTUALENV_PATH=\"{self.venv_path}\"\n')
114
+
115
+ def load_to_env_vars(self):
116
+ import dotenv
117
+ dotenv.load_dotenv(self.env_path)
118
+
119
+
120
+ class TemplateVirtualEnv:
121
+
122
+ # template requirement
123
+ TEMPLATE_REQUIREMENTS = '.requirements-template.txt'
124
+ TEMPLATE_VENV_REQUIREMENTS = 'requirements.txt'
125
+ # pyenv
126
+ PYENV_CONF_NAME = '.python-version'
127
+ BUILD_PYENV_CONF_NAME = 'build-python-version'
128
+ # poetry
129
+ POETRY_COMMAND = '$HOME/.poetry/bin/poetry'
130
+
131
+ def __init__(self, template_home, python_version=None):
132
+ self.template_home = template_home
133
+ self.py_ver = python_version
134
+
135
+ @property
136
+ def path(self):
137
+ return subprocess.getoutput(f'cd {os.path.join(self.template_home, TEMPLATE_VENV_NAME)}; {self.POETRY_COMMAND} env info -p')
138
+
139
+ @property
140
+ def py_ver(self):
141
+ return self._py_ver
142
+
143
+ @py_ver.setter
144
+ def py_ver(self, value):
145
+ if value:
146
+ self._py_ver = value
147
+ else:
148
+ # check build-python-version
149
+ python_version_path = os.path.join(self.template_home, self.BUILD_PYENV_CONF_NAME)
150
+ if os.path.isfile(python_version_path):
151
+ # use existing build-python-version setting
152
+ with open(python_version_path, 'r') as f:
153
+ self._py_ver = f.read().replace('\n', '')
154
+ else:
155
+ # default: system python version
156
+ self._py_ver = platform.python_version()
157
+
158
+ self._install_python_in_pyenv()
159
+
160
+ # write python version for pyenv
161
+ if not os.path.isdir(os.path.join(self.template_home, TEMPLATE_VENV_NAME)):
162
+ os.mkdir(os.path.join(self.template_home, TEMPLATE_VENV_NAME))
163
+
164
+ with open(os.path.join(self.template_home, TEMPLATE_VENV_NAME, self.PYENV_CONF_NAME), 'w') as text_file:
165
+ text_file.write(f'{self._py_ver}')
166
+
167
+ with open(os.path.join(self.template_home, self.BUILD_PYENV_CONF_NAME), 'w') as text_file:
168
+ text_file.write(f'{self._py_ver}')
169
+
170
+ def _install_python_in_pyenv(self):
171
+ logging.info(f'installing python {self.py_ver} via pyenv, please wait...')
172
+
173
+ command = f'pyenv install -s {self.py_ver}'
174
+ try:
175
+ logging.info(f'exec command: {command}')
176
+ subprocess.run([command], shell=True, check=True)
177
+ except Exception as e:
178
+ logging.error(e)
179
+ exit(-1)
180
+
181
+ def build(self):
182
+ base_build_command = f'cd {os.path.join(self.template_home, TEMPLATE_VENV_NAME)};'
183
+
184
+ # poetry init
185
+ init_command = f'{base_build_command} {self.POETRY_COMMAND} init --python {self._py_ver} -n'
186
+ logging.info(f'exec command: {init_command}')
187
+ os.system(init_command)
188
+
189
+ # set virtualenvs.in-project=true by project
190
+ set_command = f'{base_build_command} {self.POETRY_COMMAND} config virtualenvs.in-project true --local'
191
+ logging.info(f'exec command: {set_command}')
192
+ os.system(set_command)
193
+
194
+ # create virtualenv via specific python version (from .python-version)
195
+ venv_command = f'{base_build_command} {self.POETRY_COMMAND} env use $(pyenv which python)'
196
+ logging.info(f'exec command: {venv_command}')
197
+ os.system(venv_command)
198
+
199
+ # poetry install
200
+ install_command = f'{base_build_command} {self.POETRY_COMMAND} install'
201
+ logging.info(f'exec command: {install_command}')
202
+ os.system(install_command)
203
+
204
+ # poetry add
205
+ full_template_requirement_path = os.path.join(self.template_home, self.TEMPLATE_REQUIREMENTS)
206
+ add_command = f'{base_build_command} if [ -s {full_template_requirement_path} ] ; then cat {full_template_requirement_path} | xargs {self.POETRY_COMMAND} add ; fi'
207
+ logging.info(f'exec command: {add_command}')
208
+ os.system(add_command)
209
+
210
+ logging.info(f'create python ({self.py_ver}) venv at: {self.path}')
211
+
212
+ def install(self):
213
+ logging.info(f"install python packages ({self.TEMPLATE_VENV_REQUIREMENTS}) in virtualenv")
214
+
215
+ if self.path:
216
+ # poetry add
217
+ full_template_venv_requirement_path = os.path.join(self.template_home, 'src', self.TEMPLATE_VENV_REQUIREMENTS)
218
+ add_command = f'cd {os.path.join(self.template_home, TEMPLATE_VENV_NAME)}; if [ -s {full_template_venv_requirement_path} ] ; then cat {full_template_venv_requirement_path} | xargs {self.POETRY_COMMAND} add ; fi'
219
+ logging.info(f'exec command: {add_command}')
220
+ os.system(add_command)
221
+ else:
222
+ # pip install
223
+ install_command = f'pip install -r {self.template_home}/{self.TEMPLATE_VENV_REQUIREMENTS}'
224
+ logging.info(f'exec command: {install_command}')
225
+ os.system(install_command)
226
+
227
+ logging.info('install finished.')
@@ -0,0 +1,23 @@
1
+ root = true
2
+
3
+ [*]
4
+ indent_style = space
5
+ end_of_line = lf
6
+ charset = utf-8
7
+ trim_trailing_whitespace = true
8
+ insert_final_newline = true
9
+ indent_size = 2
10
+
11
+ [*.py]
12
+ indent_size = 4
13
+
14
+ [*.sh]
15
+ indent_size = 4
16
+
17
+ [*.md]
18
+ trim_trailing_whitespace = false
19
+
20
+ [*.rst]
21
+ indent_size = 3
22
+
23
+
@@ -0,0 +1,54 @@
1
+ # Compiled source #
2
+ ###################
3
+ *.pyc
4
+
5
+ # Logs and databases #
6
+ ######################
7
+ *.log
8
+ *.log.*
9
+ */logs
10
+
11
+ # OS generated files #
12
+ ######################
13
+ .DS_Store*
14
+
15
+ # Editor Files #
16
+ ################
17
+ *~
18
+ *.swp
19
+
20
+ # Build output directies #
21
+ ##########################
22
+ build
23
+ .eggs
24
+ *.egg-info
25
+ dist
26
+
27
+ # virtual env #
28
+ ###############
29
+ .venv
30
+ .tox
31
+
32
+ # IDE Files #
33
+ #############
34
+ .vscode/
35
+
36
+ # Cache #
37
+ #########
38
+ __pycache__
39
+ .pytest_cache
40
+ .cache
41
+
42
+ # coverage output #
43
+ ###################
44
+ .coverage
45
+ htmlcov
46
+
47
+ # Temp #
48
+ ########
49
+ tmp
50
+ temp
51
+
52
+ # Template #
53
+ .env
54
+ src/configs/vars
@@ -0,0 +1,4 @@
1
+ click==8.0.1
2
+ configparser==5.0.2
3
+ tox==3.23.1
4
+ Jinja2==2.11.3
@@ -0,0 +1,3 @@
1
+ pytest==6.2.2
2
+ pytest-cov==2.11.1
3
+ mock==4.0.3
File without changes
File without changes
@@ -0,0 +1,33 @@
1
+ import click
2
+ import logging
3
+ import time
4
+ import traceback
5
+
6
+ from src.main import Main
7
+ from utils.utils import get_configs_path, get_config_by_env
8
+
9
+ @click.command()
10
+ def receive(**kwargs):
11
+ try:
12
+ logging.info('======== Receive running request ========')
13
+ start = time.time()
14
+
15
+ # load configs
16
+ configs_path = get_configs_path()
17
+ configs = get_config_by_env(configs_path)
18
+
19
+ # trigger main task
20
+ main = Main(config=configs, input_args=kwargs)
21
+ main.run()
22
+
23
+ end = time.time()
24
+ execution_time = end - start
25
+
26
+ logging.info(f'======== Successfully! Execution time: {execution_time} sec ========')
27
+ except Exception:
28
+ error_msg = traceback.format_exc()
29
+ logging.exception(error_msg)
30
+ exit(-1)
31
+
32
+ if __name__ == '__main__':
33
+ receive()
@@ -0,0 +1,9 @@
1
+ # !/bin/bash
2
+ export APP_HOME="$(cd "`dirname "$0"`"/..; pwd)"
3
+ export PYTHONPATH=${APP_HOME}/src:$PYTHONPATH
4
+
5
+ . "${APP_HOME}/.env"
6
+
7
+ cd ${APP_HOME}
8
+
9
+ python ${APP_HOME}/bin/start.py $@
@@ -0,0 +1,11 @@
1
+ import os
2
+ import sys
3
+ import logging
4
+ from logging import config
5
+
6
+ if hasattr(sys, '_called_from_test'):
7
+ # called from within a test run
8
+ pass
9
+ else:
10
+ logging.logfilename = os.environ.get('KKPY_PROJECT_NAME', 'app')
11
+ config.fileConfig('src/configs/logging_config.ini')
@@ -0,0 +1,9 @@
1
+ [prod]
2
+
3
+ [stage]
4
+
5
+ [sit]
6
+
7
+ [dev]
8
+ test_config=This Is Test
9
+ set_from_vars_file={{ user_name }}
@@ -0,0 +1,5 @@
1
+ [database]
2
+ host = {{ db_host }}
3
+ user = {{ db_user }}
4
+ password = {{ db_password }}
5
+ dbname = {{ db_name }}
@@ -0,0 +1,27 @@
1
+ [loggers]
2
+ keys=root
3
+
4
+ [handlers]
5
+ keys=stdout,filelog
6
+
7
+ [formatters]
8
+ keys=default
9
+
10
+ [logger_root]
11
+ level=INFO
12
+ handlers=filelog,stdout
13
+
14
+ [handler_stdout]
15
+ class=StreamHandler
16
+ level=DEBUG
17
+ formatter=default
18
+ args=(sys.stdout,)
19
+
20
+ [handler_filelog]
21
+ class=handlers.RotatingFileHandler
22
+ level=DEBUG
23
+ args=('logs/'+logfilename+'.log', 'a', 10000000, 10)
24
+ formatter=default
25
+
26
+ [formatter_default]
27
+ format=%(asctime)s-[%(threadName)s][%(thread)x]-%(filename)-8s-%(name)-8s-%(funcName)-10s-%(levelname)-5s-%(lineno)d: %(message)s
@@ -0,0 +1,27 @@
1
+ import logging
2
+ import os
3
+ import sys
4
+
5
+ logger = logging.getLogger(__name__)
6
+
7
+
8
+ class Main(object):
9
+
10
+ def __init__(self, config, **kwargs):
11
+ self.config = config
12
+ self.args = kwargs.get('input_args')
13
+
14
+ def run(self):
15
+ try:
16
+ logger.info('>>> run')
17
+ logger.info(f'config(app): {self.config["app"]}')
18
+ logger.info(f'input args: {self.args}')
19
+
20
+ # TODO Let's start coding for the project !
21
+
22
+ except Exception:
23
+ raise
24
+
25
+ # ===== Just for testing test feature =====
26
+ def just_test_fun():
27
+ return True
File without changes
File without changes
@@ -0,0 +1,63 @@
1
+ import logging
2
+ import os
3
+ from configparser import ConfigParser
4
+
5
+ from jinja2 import Environment, FileSystemLoader
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+ CONF_BASE_PATH = 'src/configs'
10
+ EXTRA_VAR_FILE_NAME = 'vars'
11
+
12
+ jinja_env = Environment(loader=FileSystemLoader('.'))
13
+
14
+ def get_configs_path():
15
+ exclude_list = ['logging_config.ini']
16
+ return [f'{CONF_BASE_PATH}/{f}'
17
+ for f in os.listdir(CONF_BASE_PATH) if f.endswith('.ini') and f not in exclude_list]
18
+
19
+ def _load_vars_from_file():
20
+ extra_var_file_path = os.path.join(CONF_BASE_PATH, EXTRA_VAR_FILE_NAME)
21
+
22
+ udf_vars = {}
23
+ if os.path.exists(extra_var_file_path):
24
+ with open(extra_var_file_path, 'r') as f:
25
+ for line in f:
26
+ line = line.rstrip('\n')
27
+ if line != '':
28
+ k, v = line.split('=', 1)
29
+ udf_vars[k.strip()] = v.strip()
30
+
31
+ return udf_vars
32
+
33
+ def get_config_by_env(configs_path):
34
+ try:
35
+ current_env = os.environ.get('KKPY_ENV', 'dev')
36
+
37
+ # load vars from vars file
38
+ udf_vars = _load_vars_from_file()
39
+
40
+ configs_by_file_name = {}
41
+ for path in configs_path:
42
+
43
+ file_name = os.path.basename(path).rstrip('.ini')
44
+
45
+ # load .ini as a template
46
+ conf_template = jinja_env.get_template(f'{CONF_BASE_PATH}/{file_name}.ini')
47
+
48
+ parser = ConfigParser()
49
+ parser.read_string(conf_template.render(**udf_vars)) # render vars to config template(.ini)
50
+
51
+ options = {}
52
+ if file_name == 'app' \
53
+ and parser.has_section(current_env): # load runtime env section (dev|sit|stage|prod) in app.ini
54
+ options = dict(parser.items(current_env))
55
+ else: # load all sections in other .ini files
56
+ for section_key in parser.sections():
57
+ options[section_key] = dict(parser.items(section_key))
58
+
59
+ configs_by_file_name[file_name] = options
60
+ except Exception:
61
+ raise
62
+ else:
63
+ return configs_by_file_name
File without changes
@@ -0,0 +1,9 @@
1
+ import sys
2
+
3
+ def pytest_configure(config):
4
+
5
+ sys._called_from_test = True
6
+
7
+ def pytest_unconfigure(config):
8
+
9
+ del sys._called_from_test
@@ -0,0 +1,4 @@
1
+ from src.main import just_test_fun
2
+
3
+ def test_fun():
4
+ assert just_test_fun()
kkpy/templates/tox.ini ADDED
@@ -0,0 +1,27 @@
1
+ [tox]
2
+ skipsdist = True
3
+
4
+ [testenv]
5
+ setenv =
6
+ PYTHONPATH = {toxinidir}/src
7
+ PYTHONDONTWRITEBYTECODE = 1
8
+
9
+ deps =
10
+ -r{toxinidir}/src/requirements.txt
11
+ -r{toxinidir}/.requirements-test.txt
12
+ -r{toxinidir}/.requirements-template.txt
13
+
14
+ commands =
15
+ - pytest \
16
+ --cov-config {toxinidir}/tox.ini \
17
+ --color yes \
18
+ --cov {toxinidir}/src \
19
+ -vvv
20
+
21
+ [coverage:run]
22
+ omit =
23
+ */__init__.py
24
+
25
+ [pytest]
26
+ filterwarnings =
27
+ ignore:: DeprecationWarning
kkpy_cli/__init__.py ADDED
File without changes