diff --git a/venv/Lib/site-packages/Menus-0.2.0.dist-info/DESCRIPTION.rst b/venv/Lib/site-packages/Menus-0.2.0.dist-info/DESCRIPTION.rst new file mode 100644 index 00000000..e1187231 --- /dev/null +++ b/venv/Lib/site-packages/Menus-0.2.0.dist-info/DESCRIPTION.rst @@ -0,0 +1,3 @@ +UNKNOWN + + diff --git a/venv/Lib/site-packages/Menus-0.2.0.dist-info/INSTALLER b/venv/Lib/site-packages/Menus-0.2.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/Lib/site-packages/Menus-0.2.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/Menus-0.2.0.dist-info/METADATA b/venv/Lib/site-packages/Menus-0.2.0.dist-info/METADATA new file mode 100644 index 00000000..3ca139f4 --- /dev/null +++ b/venv/Lib/site-packages/Menus-0.2.0.dist-info/METADATA @@ -0,0 +1,15 @@ +Metadata-Version: 2.0 +Name: Menus +Version: 0.2.0 +Summary: Create cli menus with ease +Home-page: https://github.com/JMSwag/Menus +Author: Digital Sapphire +Author-email: menus@digitalsapphire.io +License: MIT +Platform: UNKNOWN +Requires-Dist: dsdev-utils +Requires-Dist: six + +UNKNOWN + + diff --git a/venv/Lib/site-packages/Menus-0.2.0.dist-info/RECORD b/venv/Lib/site-packages/Menus-0.2.0.dist-info/RECORD new file mode 100644 index 00000000..70eb75ee --- /dev/null +++ b/venv/Lib/site-packages/Menus-0.2.0.dist-info/RECORD @@ -0,0 +1,24 @@ +Menus-0.2.0.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10 +Menus-0.2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Menus-0.2.0.dist-info/METADATA,sha256=5E660I04WM6yX5M3KfFsqJZNz5l4kckz3MFAou_1Iu0,280 +Menus-0.2.0.dist-info/RECORD,, +Menus-0.2.0.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 +Menus-0.2.0.dist-info/metadata.json,sha256=k5fJgBoAIoSIFjuR1yUm6YALlY9n5FTybcF8RqhDLjQ,480 +Menus-0.2.0.dist-info/top_level.txt,sha256=mr-Mm53IP5nmAdXmK0MURdgtX5aoNBmIrgHgyPdKUac,11 +Menus-0.2.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 +menus/__init__.py,sha256=Racmika2iOJjIGPzbSZs8hQARcJaif_N2rLyz0uUGJY,1419 +menus/__pycache__/__init__.cpython-36.pyc,, +menus/__pycache__/_version.cpython-36.pyc,, +menus/__pycache__/engine.cpython-36.pyc,, +menus/__pycache__/example.cpython-36.pyc,, +menus/__pycache__/exceptions.cpython-36.pyc,, +menus/__pycache__/menu.cpython-36.pyc,, +menus/__pycache__/utils.cpython-36.pyc,, +menus/_version.py,sha256=Z0DxOc2U4ydSV42gG9XZ6DRszh-5f53SRi3SAzpPxKU,471 +menus/engine.py,sha256=tG6395XNJ4LcKzE76hdcDGGrOFPRYrOXdV6pjIN9Rb0,3315 +menus/example.py,sha256=tLYJJTtloPBfFc3kui6s-d_nPAXyEdGs8gqjssHOIpA,3292 +menus/exceptions.py,sha256=Widb24ezSJNpMy8Dt8Wi_xQmXA561x3cI4hj27Br2_4,2400 +menus/menu.py,sha256=kMzIcnwPsFHQLEN2NKQ0m4EUqTlzBKNlVnje7tJ6Cbs,6983 +menus/utils.py,sha256=ReoV8xbA9SjX6psiSPevG-A7NVJWQLmz0rVw7MLuPIQ,3670 +site/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +site/__pycache__/__init__.cpython-36.pyc,, diff --git a/venv/Lib/site-packages/Menus-0.2.0.dist-info/WHEEL b/venv/Lib/site-packages/Menus-0.2.0.dist-info/WHEEL new file mode 100644 index 00000000..8b6dd1b5 --- /dev/null +++ b/venv/Lib/site-packages/Menus-0.2.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.29.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/Lib/site-packages/Menus-0.2.0.dist-info/metadata.json b/venv/Lib/site-packages/Menus-0.2.0.dist-info/metadata.json new file mode 100644 index 00000000..3fb5f9a4 --- /dev/null +++ b/venv/Lib/site-packages/Menus-0.2.0.dist-info/metadata.json @@ -0,0 +1 @@ +{"extensions": {"python.details": {"contacts": [{"email": "menus@digitalsapphire.io", "name": "Digital Sapphire", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/JMSwag/Menus"}}}, "extras": [], "generator": "bdist_wheel (0.29.0)", "license": "MIT", "metadata_version": "2.0", "name": "Menus", "run_requires": [{"requires": ["dsdev-utils", "six"]}], "summary": "Create cli menus with ease", "version": "0.2.0"} \ No newline at end of file diff --git a/venv/Lib/site-packages/Menus-0.2.0.dist-info/top_level.txt b/venv/Lib/site-packages/Menus-0.2.0.dist-info/top_level.txt new file mode 100644 index 00000000..39a88e33 --- /dev/null +++ b/venv/Lib/site-packages/Menus-0.2.0.dist-info/top_level.txt @@ -0,0 +1,2 @@ +menus +site diff --git a/venv/Lib/site-packages/Menus-0.2.0.dist-info/zip-safe b/venv/Lib/site-packages/Menus-0.2.0.dist-info/zip-safe new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/venv/Lib/site-packages/Menus-0.2.0.dist-info/zip-safe @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/PKG-INFO b/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/PKG-INFO new file mode 100644 index 00000000..112d8913 --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/PKG-INFO @@ -0,0 +1,17 @@ +Metadata-Version: 1.1 +Name: dsdev-utils +Version: 1.0.4 +Summary: Various utility functions +Home-page: https://github.com/JMSwag/dsdev-utils +Author: Digital Sapphire +Author-email: digitalsapphire@gmail.com +License: MIT +Download-URL: https://github.com/JMSwag/dsdev-utils/archive/master.zip +Description: UNKNOWN +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Operating System :: OS Independent +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python diff --git a/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/SOURCES.txt b/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/SOURCES.txt new file mode 100644 index 00000000..4fbb83d9 --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/SOURCES.txt @@ -0,0 +1,22 @@ +MANIFEST.in +README.md +setup.cfg +setup.py +versioneer.py +dsdev_utils/__init__.py +dsdev_utils/_version.py +dsdev_utils/app.py +dsdev_utils/compat.py +dsdev_utils/config.py +dsdev_utils/crypto.py +dsdev_utils/exceptions.py +dsdev_utils/helpers.py +dsdev_utils/logger.py +dsdev_utils/paths.py +dsdev_utils/system.py +dsdev_utils/terminal.py +dsdev_utils.egg-info/PKG-INFO +dsdev_utils.egg-info/SOURCES.txt +dsdev_utils.egg-info/dependency_links.txt +dsdev_utils.egg-info/requires.txt +dsdev_utils.egg-info/top_level.txt \ No newline at end of file diff --git a/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/dependency_links.txt b/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/dependency_links.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/installed-files.txt b/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/installed-files.txt new file mode 100644 index 00000000..f1035f25 --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/installed-files.txt @@ -0,0 +1,29 @@ +..\dsdev_utils\__init__.py +..\dsdev_utils\__pycache__\__init__.cpython-36.pyc +..\dsdev_utils\__pycache__\_version.cpython-36.pyc +..\dsdev_utils\__pycache__\app.cpython-36.pyc +..\dsdev_utils\__pycache__\compat.cpython-36.pyc +..\dsdev_utils\__pycache__\config.cpython-36.pyc +..\dsdev_utils\__pycache__\crypto.cpython-36.pyc +..\dsdev_utils\__pycache__\exceptions.cpython-36.pyc +..\dsdev_utils\__pycache__\helpers.cpython-36.pyc +..\dsdev_utils\__pycache__\logger.cpython-36.pyc +..\dsdev_utils\__pycache__\paths.cpython-36.pyc +..\dsdev_utils\__pycache__\system.cpython-36.pyc +..\dsdev_utils\__pycache__\terminal.cpython-36.pyc +..\dsdev_utils\_version.py +..\dsdev_utils\app.py +..\dsdev_utils\compat.py +..\dsdev_utils\config.py +..\dsdev_utils\crypto.py +..\dsdev_utils\exceptions.py +..\dsdev_utils\helpers.py +..\dsdev_utils\logger.py +..\dsdev_utils\paths.py +..\dsdev_utils\system.py +..\dsdev_utils\terminal.py +PKG-INFO +SOURCES.txt +dependency_links.txt +requires.txt +top_level.txt diff --git a/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/requires.txt b/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/requires.txt new file mode 100644 index 00000000..8190f4bf --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/requires.txt @@ -0,0 +1,2 @@ +chardet +six diff --git a/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/top_level.txt b/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/top_level.txt new file mode 100644 index 00000000..f51e186d --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils-1.0.4-py3.6.egg-info/top_level.txt @@ -0,0 +1 @@ +dsdev_utils diff --git a/venv/Lib/site-packages/dsdev_utils/__init__.py b/venv/Lib/site-packages/dsdev_utils/__init__.py new file mode 100644 index 00000000..86a726ef --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils/__init__.py @@ -0,0 +1,26 @@ +# -------------------------------------------------------------------------- +# The MIT License (MIT) +# +# Copyright (c) 2014-2016 Digital Sapphire +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# -------------------------------------------------------------------------- +from ._version import get_versions +__version__ = get_versions()['version'] +del get_versions diff --git a/venv/Lib/site-packages/dsdev_utils/_version.py b/venv/Lib/site-packages/dsdev_utils/_version.py new file mode 100644 index 00000000..c70e7c8d --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils/_version.py @@ -0,0 +1,21 @@ + +# This file was generated by 'versioneer.py' (0.16) from +# revision-control system data, or from the parent directory name of an +# unpacked source archive. Distribution tarballs contain a pre-generated copy +# of this file. + +import json +import sys + +version_json = ''' +{ + "dirty": false, + "error": null, + "full-revisionid": "fddc3e711787229b373a323759589a71f58722dd", + "version": "1.0.4" +} +''' # END VERSION_JSON + + +def get_versions(): + return json.loads(version_json) diff --git a/venv/Lib/site-packages/dsdev_utils/app.py b/venv/Lib/site-packages/dsdev_utils/app.py new file mode 100644 index 00000000..a466bcfa --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils/app.py @@ -0,0 +1,47 @@ +# ------------------------------------------------------------------------------ +# The MIT License (MIT) +# +# Copyright (c) 2014-2019 Digital Sapphire +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# ------------------------------------------------------------------------------ +import logging +import os +import sys + +log = logging.getLogger(__name__) + +FROZEN = getattr(sys, u'frozen', False) + + +def _app_cwd(): + if FROZEN: # pragma: no cover + # we are running in a |PyInstaller| bundle + cwd = os.path.dirname(sys.argv[0]) + if cwd.endswith('MacOS') is True: + log.debug('Looks like we\'re dealing with a Mac Gui') + cwd = os.path.dirname(os.path.dirname(os.path.dirname(cwd))) + + else: + # we are running in a normal Python environment + cwd = os.getcwd() + return cwd + + +app_cwd = _app_cwd() diff --git a/venv/Lib/site-packages/dsdev_utils/compat.py b/venv/Lib/site-packages/dsdev_utils/compat.py new file mode 100644 index 00000000..a360705d --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils/compat.py @@ -0,0 +1,66 @@ +# ------------------------------------------------------------------------------ +# The MIT License (MIT) +# +# Copyright (c) 2014-2019 Digital Sapphire +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# ------------------------------------------------------------------------------ +import chardet +import logging + +import six + +if not six.PY2: + # Helper for Python 2 and 3 compatibility + unicode = str + +log = logging.getLogger(__name__) + + +def make_compat_str(in_str): + """ + Tries to guess encoding of [str/bytes] and decode it into + an unicode object. + """ + assert isinstance(in_str, (bytes, str, unicode)) + if not in_str: + return unicode() + + # Chardet in Py2 works on str + bytes objects + if six.PY2 and isinstance(in_str, unicode): + return in_str + + # Chardet in Py3 works on bytes objects + if not six.PY2 and not isinstance(in_str, bytes): + return in_str + + # Detect the encoding now + enc = chardet.detect(in_str) + + # Decode the object into a unicode object + out_str = in_str.decode(enc['encoding']) + + # Cleanup: Sometimes UTF-16 strings include the BOM + if enc['encoding'] == "UTF-16BE": + # Remove byte order marks (BOM) + if out_str.startswith('\ufeff'): + out_str = out_str[1:] + + # Return the decoded string + return out_str diff --git a/venv/Lib/site-packages/dsdev_utils/config.py b/venv/Lib/site-packages/dsdev_utils/config.py new file mode 100644 index 00000000..accae0b1 --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils/config.py @@ -0,0 +1,61 @@ +# ------------------------------------------------------------------------------ +# The MIT License (MIT) +# +# Copyright (c) 2014-2019 Digital Sapphire +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# ------------------------------------------------------------------------------ +import logging + +log = logging.getLogger(__name__) + + +class ConfigDict(dict): + def __init__(self, *args, **kwargs): + super(ConfigDict, self).__init__(*args, **kwargs) + self.__dict__ = self + default = kwargs.get('default', {}) + assert isinstance(default, dict) + self.update(default) + + def update(self, data): + _data = {} + for k, v in data.items(): + if k.isupper(): + _data[k] = v + super(ConfigDict, self).update(_data) + + def from_object(self, obj): + """Updates the values from the given object + + Args: + + obj (instance): Object with config attributes + + Objects are classes. + + Just the uppercase variables in that object are stored in the config. + Example usage:: + + from yourapplication import default_config + app.config.from_object(default_config()) + """ + for key in dir(obj): + if key.isupper(): + self[key] = getattr(obj, key) diff --git a/venv/Lib/site-packages/dsdev_utils/crypto.py b/venv/Lib/site-packages/dsdev_utils/crypto.py new file mode 100644 index 00000000..569cab85 --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils/crypto.py @@ -0,0 +1,49 @@ +# ------------------------------------------------------------------------------ +# The MIT License (MIT) +# +# Copyright (c) 2014-2019 Digital Sapphire +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# ------------------------------------------------------------------------------ +import hashlib +import logging +import os + +log = logging.getLogger(__name__) + + +def get_package_hashes(filename): + """Provides hash of given filename. + + Args: + + filename (str): Name of file to hash + + Returns: + + (str): sha256 hash + """ + log.debug('Getting package hashes') + filename = os.path.abspath(filename) + with open(filename, 'rb') as f: + data = f.read() + + _hash = hashlib.sha256(data).hexdigest() + log.debug('Hash for file %s: %s', filename, _hash) + return _hash diff --git a/venv/Lib/site-packages/dsdev_utils/exceptions.py b/venv/Lib/site-packages/dsdev_utils/exceptions.py new file mode 100644 index 00000000..18b9f498 --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils/exceptions.py @@ -0,0 +1,68 @@ +# ------------------------------------------------------------------------------ +# The MIT License (MIT) +# +# Copyright (c) 2014-2019 Digital Sapphire +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# ------------------------------------------------------------------------------ +import logging +import sys +import traceback + +log = logging.getLogger(__name__) + + +class STDError(Exception): + """Extends exceptions to show added message if error isn't expected. + + Args: + + msg (str): error message + + Kwargs: + + tb (obj): is the original traceback so that it can be printed. + + expected (bool): + + Meaning: + + True - Report issue msg not shown + + False - Report issue msg shown + """ + def __init__(self, msg, tb=None, expected=False): + if expected is False: + msg = msg + ('; please report this issue on https://github.com' + '/JMSwag/dsdev-utils/issues') + super(STDError, self).__init__(msg) + + self.traceback = tb + self.exc_info = sys.exc_info() # preserve original exception + + def format_traceback(self): + if self.traceback is None: + return None + return ''.join(traceback.format_tb(self.traceback)) + + +class VersionError(STDError): + """Raised for Utils exceptions""" + def __init__(self, *args, **kwargs): + super(VersionError, self).__init__(*args, **kwargs) diff --git a/venv/Lib/site-packages/dsdev_utils/helpers.py b/venv/Lib/site-packages/dsdev_utils/helpers.py new file mode 100644 index 00000000..5341c4ff --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils/helpers.py @@ -0,0 +1,305 @@ +# ------------------------------------------------------------------------------ +# The MIT License (MIT) +# +# Copyright (c) 2014-2019 Digital Sapphire +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# ------------------------------------------------------------------------------ +import io +import gzip +import logging +import os +import re +import sys + +from dsdev_utils.exceptions import VersionError + +log = logging.getLogger(__name__) + + +# Decompress gzip data +# +# Args: +# +# data (str): Gzip data +# +# +# Returns: +# +# (data): Decompressed data +def gzip_decompress(data): + # if isinstance(data, six.binary_type): + # data = data.decode() + compressed_file = io.BytesIO() + compressed_file.write(data) + # + # Set the file's current position to the beginning + # of the file so that gzip.GzipFile can read + # its contents from the top. + # + compressed_file.seek(0) + decompressed_file = gzip.GzipFile(fileobj=compressed_file, mode='rb') + data = decompressed_file.read() + compressed_file.close() + decompressed_file.close() + return data + + +def lazy_import(func): + """Decorator for declaring a lazy import. + + This decorator turns a function into an object that will act as a lazy + importer. Whenever the object's attributes are accessed, the function + is called and its return value used in place of the object. So you + can declare lazy imports like this: + + @lazy_import + def socket(): + import socket + return socket + + The name "socket" will then be bound to a transparent object proxy which + will import the socket module upon first use. + + The syntax here is slightly more verbose than other lazy import recipes, + but it's designed not to hide the actual "import" statements from tools + like pyinstaller or grep. + """ + try: + f = sys._getframe(1) + except Exception: # pragma: no cover + namespace = None + else: + namespace = f.f_locals + return _LazyImport(func.__name__, func, namespace) + + +class _LazyImport(object): + """Class representing a lazy import.""" + + def __init__(self, name, loader, namespace=None): + self._dsdev_lazy_target = _LazyImport + self._dsdev_lazy_name = name + self._dsdev_lazy_loader = loader + self._dsdev_lazy_namespace = namespace + + def _dsdev_lazy_load(self): + if self._dsdev_lazy_target is _LazyImport: + self._dsdev_lazy_target = self._dsdev_lazy_loader() + ns = self._dsdev_lazy_namespace + if ns is not None: + try: + if ns[self._dsdev_lazy_name] is self: + ns[self._dsdev_lazy_name] = self._dsdev_lazy_target + except KeyError: # pragma: no cover + pass + + def __getattribute__(self, attr): # pragma: no cover + try: + return object.__getattribute__(self, attr) + except AttributeError: + if self._dsdev_lazy_target is _LazyImport: + self._dsdev_lazy_load() + return getattr(self._dsdev_lazy_target, attr) + + def __nonzero__(self): # pragma: no cover + if self._dsdev_lazy_target is _LazyImport: + self._dsdev_lazy_load() + return bool(self._dsdev_lazy_target) + + def __str__(self): # pragma: no cover + return '_LazyImport: {}'.format(self._dsdev_lazy_name) + + +# Normalizes version strings of different types. Examples +# include 1.2, 1.2.1, 1.2b and 1.1.1b +# +# Args: +# +# version (str): Version number to normalizes +class Version(object): + + v_re = re.compile(r'(?P\d+)\.(?P\d+)\.?(?P' + r'\d+)?-?(?P[abehl' + r'pt]+)?-?(?P\d+)?') + + v_re_big = re.compile(r'(?P\d+)\.(?P\d+)\.' + r'(?P\d+)\.(?P\d+)' + r'\.(?P\d+)') + + def __init__(self, version): + self.original_version = version + self._parse_version_str(version) + self.version_str = None + + def _parse_version_str(self, version): + count = self._quick_sanitize(version) + try: + # version in the form of 1.1, 1.1.1, 1.1.1-b1, 1.1.1a2 + if count == 4: + version_data = self._parse_parsed_version(version) + else: + version_data = self._parse_version(version) + except AssertionError: + raise VersionError('Cannot parse version') + + self.major = int(version_data.get('major', 0)) + self.minor = int(version_data.get('minor', 0)) + patch = version_data.get('patch') + if patch is None: + self.patch = 0 + else: + self.patch = int(patch) + release = version_data.get('release') + self.channel = 'stable' + if release is None: + self.release = 2 + # Convert to number for easy comparison and sorting + elif release in ['b', 'beta', '1']: + self.release = 1 + self.channel = 'beta' + elif release in ['a', 'alpha', '0']: + self.release = 0 + self.channel = 'alpha' + else: + log.debug('Setting release as stable. ' + 'Disregard if not prerelease') + # Marking release as stable + self.release = 2 + + release_version = version_data.get('releaseversion') + if release_version is None: + self.release_version = 0 + else: + self.release_version = int(release_version) + self.version_tuple = (self.major, self.minor, self.patch, + self.release, self.release_version) + self.version_str = str(self.version_tuple) + + def _parse_version(self, version): + r = self.v_re.search(version) + assert r is not None + return r.groupdict() + + def _parse_parsed_version(self, version): + r = self.v_re_big.search(version) + assert r is not None + return r.groupdict() + + @staticmethod + def _quick_sanitize(version): + log.debug('Version str: %s', version) + ext = os.path.splitext(version)[1] + # Removing file extensions, to ensure count isn't + # contaminated + if ext == '.zip': + log.debug('Removed ".zip"') + version = version[:-4] + elif ext == '.gz': + log.debug('Removed ".tar.gz"') + version = version[:-7] + elif ext == '.bz2': + log.debug('Removed ".tar.bz2"') + version = version[:-8] + count = version.count('.') + # There will be 4 dots when version is passed + # That was created with Version object. + # 1.1 once parsed will be 1.1.0.0.0 + if count not in [1, 2, 4]: + msg = ('Incorrect version format. 1 or 2 dots ' + 'You have {} dots'.format(count)) + log.debug(msg) + raise VersionError(msg) + return count + + def __str__(self): + return '.'.join(map(str, self.version_tuple)) + + def __repr__(self): + return '{}: {}'.format(self.__class__.__name__, + self.version_str) + + def __hash__(self): + return hash(self.version_tuple) + + def __eq__(self, obj): + return self.version_tuple == obj.version_tuple + + def __ne__(self, obj): + return self.version_tuple != obj.version_tuple + + def __lt__(self, obj): + return self.version_tuple < obj.version_tuple + + def __gt__(self, obj): + return self.version_tuple > obj.version_tuple + + def __le__(self, obj): + return self.version_tuple <= obj.version_tuple + + def __ge__(self, obj): + return self.version_tuple >= obj.version_tuple + + +# Provides access to dict by pass a specially made key to +# the get method. Default key sep is "*". Example key would be +# updates*mac*1.7.0 would access {"updates":{"mac":{"1.7.0": "hi there"}}} +# and return "hi there" +# +# Kwargs: +# +# dict_ (dict): Dict you would like easy asses to. +# +# sep (str): Used as a delimiter between keys +class EasyAccessDict(object): + + def __init__(self, dict_=None, sep='*'): + self.sep = sep + if not isinstance(dict_, dict): + self.dict = {} + else: + self.dict = dict_ + + # Retrive value from internal dict. + # + # args: + # + # key (str): Key to access value + # + # Returns: + # + # (object): Value of key if found or None + def get(self, key): + try: + layers = key.split(self.sep) + value = self.dict + for key in layers: + value = value[key] + return value + except KeyError: + return None + except Exception: # pragma: no cover + return None + + # Because I always forget call the get method + def __call__(self, key): + return self.get(key) + + def __str__(self): + return str(self.dict) diff --git a/venv/Lib/site-packages/dsdev_utils/logger.py b/venv/Lib/site-packages/dsdev_utils/logger.py new file mode 100644 index 00000000..1aaa4fa2 --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils/logger.py @@ -0,0 +1,28 @@ +# ------------------------------------------------------------------------------ +# The MIT License (MIT) +# +# Copyright (c) 2014-2019 Digital Sapphire +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# ------------------------------------------------------------------------------ +import logging + + +logging_formatter = logging.Formatter(u'[%(levelname)s] %(name)s ' + u'%(lineno)d: %(message)s') diff --git a/venv/Lib/site-packages/dsdev_utils/paths.py b/venv/Lib/site-packages/dsdev_utils/paths.py new file mode 100644 index 00000000..c29047fd --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils/paths.py @@ -0,0 +1,96 @@ +# ------------------------------------------------------------------------------ +# The MIT License (MIT) +# +# Copyright (c) 2014-2019 Digital Sapphire +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# ------------------------------------------------------------------------------ +import logging +import os +import shutil +import sys +import time + +import six + + +log = logging.getLogger(__name__) + + +def get_mac_dot_app_dir(directory): + """Returns parent directory of mac .app + + Args: + + directory (str): Current directory + + Returns: + + (str): Parent directory of mac .app + """ + return os.path.dirname(os.path.dirname(os.path.dirname(directory))) + + +def remove_any(path): + if six.PY2 or sys.version_info[1] == 5: + path = str(path) + + if not os.path.exists(path): + return + + def _remove_any(x): + if os.path.isdir(x): + shutil.rmtree(x, ignore_errors=True) + else: + os.remove(path) + + if sys.platform != 'win32': + _remove_any(path) + else: + for _ in range(100): + try: + _remove_any(path) + except Exception as err: + log.debug(err, exc_info=True) + time.sleep(0.01) + else: + break + else: + try: + _remove_any(path) + except Exception as err: + log.debug(err, exc_info=True) + + +class ChDir(object): + + def __init__(self, path): + if six.PY2 or sys.version_info[1] in [4, 5]: + path = str(path) + + self.old_dir = os.getcwd() + self.new_dir = path + + def __enter__(self): + log.debug('Changing to Directory --> {}'.format(self.new_dir)) + os.chdir(self.new_dir) + + def __exit__(self, *args, **kwargs): + log.debug('Moving back to Directory --> {}'.format(self.old_dir)) + os.chdir(self.old_dir) diff --git a/venv/Lib/site-packages/dsdev_utils/system.py b/venv/Lib/site-packages/dsdev_utils/system.py new file mode 100644 index 00000000..951dfe9d --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils/system.py @@ -0,0 +1,64 @@ +# ------------------------------------------------------------------------------ +# The MIT License (MIT) +# +# Copyright (c) 2014-2019 Digital Sapphire +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# ------------------------------------------------------------------------------ +from __future__ import unicode_literals +import logging +import platform +import sys + +log = logging.getLogger(__name__) + +_PLATFORM = None +_ARCHITECTURE = None + + +def get_architecure(): + global _ARCHITECTURE + if _ARCHITECTURE is not None: + return _ARCHITECTURE + if '64bit' in platform.architecture()[0]: + _ARCHITECTURE = '64' + else: + _ARCHITECTURE = '32' + return _ARCHITECTURE + + +def get_system(): + global _PLATFORM + if _PLATFORM is not None: + return _PLATFORM + if sys.platform == 'win32': + _PLATFORM = 'win' + elif sys.platform == 'darwin': + _PLATFORM = 'mac' + else: + arch = get_architecure() + if 'arm' in platform.uname()[4]: + _PLATFORM = 'arm' + if arch == '64': + _PLATFORM += arch + else: + _PLATFORM = 'nix' + if arch == '64': + _PLATFORM += arch + return _PLATFORM diff --git a/venv/Lib/site-packages/dsdev_utils/terminal.py b/venv/Lib/site-packages/dsdev_utils/terminal.py new file mode 100644 index 00000000..68160a67 --- /dev/null +++ b/venv/Lib/site-packages/dsdev_utils/terminal.py @@ -0,0 +1,284 @@ +# ------------------------------------------------------------------------------ +# The MIT License (MIT) +# +# Copyright (c) 2014-2019 Digital Sapphire +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# ------------------------------------------------------------------------------ +from __future__ import print_function +import logging +try: + import msvcrt +except ImportError: + msvcrt = None +import locale +import optparse +import os +import platform +import shlex +import struct +import subprocess +import sys +try: + import termios +except ImportError: + termios = None +try: + import tty +except ImportError: + tty = None + +import six + +log = logging.getLogger(__name__) + + +def print_to_console(text): + enc = locale.getdefaultlocale()[1] or "utf-8" + try: + print(text.encode(enc, errors="backslashreplace")) + except (LookupError, UnicodeEncodeError): + # Unknown encoding or encoding problem. Fallback to ascii + print(text.encode("ascii", errors="backslashreplace")) + + +def terminal_formatter(): + max_width = 80 + max_help_position = 80 + + # No need to wrap help messages if we're on a wide console + columns = get_terminal_size()[0] + if columns: + max_width = columns + + fmt = optparse.IndentedHelpFormatter(width=max_width, + max_help_position=max_help_position) + return fmt + + +# get width and height of console +# works on linux, os x, windows, cygwin(windows) +# originally retrieved from: +# http://stackoverflow.com/questions/ +# 566746/how-to-get-console-window-width-in-python +def get_terminal_size(): + current_os = platform.system() + tuple_xy = None + if current_os == u'Windows': + tuple_xy = _get_terminal_size_windows() + if tuple_xy is None: + tuple_xy = _get_terminal_size_tput() + # needed for window's python in cygwin's xterm! + if current_os in [u'Linux', u'Darwin'] or current_os.startswith('CYGWIN'): + tuple_xy = _get_terminal_size_linux() + if tuple_xy is None: + log.debug(u"default") + tuple_xy = (80, 25) # default value + return tuple_xy + + +def _get_terminal_size_windows(): + try: + from ctypes import windll, create_string_buffer + # stdin handle is -10 + # stdout handle is -11 + # stderr handle is -12 + h = windll.kernel32.GetStdHandle(-12) + csbi = create_string_buffer(22) + res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) + if res: + (bufx, bufy, curx, cury, wattr, + left, top, right, bottom, + maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw) + sizex = right - left + 1 + sizey = bottom - top + 1 + return sizex, sizey + except: + pass + + +def _get_terminal_size_tput(): + # get terminal width + # http://stackoverflow.com/questions/263890/ + # how-do-i-find-the-width-height-of-a-terminal-window + try: + cols = int(subprocess.check_call(shlex.split('tput cols'))) + rows = int(subprocess.check_call(shlex.split('tput lines'))) + return (cols, rows) + except: + pass + + +def _get_terminal_size_linux(): + def ioctl_GWINSZ(fd): + try: + import fcntl + # Is this required + # import termios + cr = struct.unpack('hh', + fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) + return cr + except: + pass + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + if not cr: + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + cr = ioctl_GWINSZ(fd) + os.close(fd) + except: + pass + if not cr: + try: + cr = (os.environ['LINES'], os.environ['COLUMNS']) + except: + return None + return int(cr[1]), int(cr[0]) + + +# Gets a single character form standard input. Does not echo to the screen +class GetCh: + + def __init__(self): + if sys.platform == u'win32': + self.impl = _GetchWindows() + else: + self.impl = _GetchUnix() + + def __call__(self): + return self.impl() + + +class _GetchUnix: + def __init__(self): + pass + + def __call__(self): + pass + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(sys.stdin.fileno()) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + +class _GetchWindows: + def __init__(self): + pass + + def __call__(self): + return msvcrt.getch() + + +def ask_yes_no(question, default='no', answer=None): + u"""Will ask a question and keeps prompting until + answered. + + Args: + + question (str): Question to ask end user + + default (str): Default answer if user just press enter at prompt + + answer (str): Used for testing + + Returns: + + (bool) Meaning: + + True - Answer is yes + + False - Answer is no + """ + default = default.lower() + yes = [u'yes', u'ye', u'y'] + no = [u'no', u'n'] + if default in no: + help_ = u'[N/y]?' + default = False + else: + default = True + help_ = u'[Y/n]?' + while 1: + display = question + '\n' + help_ + if answer is None: + log.debug(u'Under None') + answer = six.moves.input(display) + answer = answer.lower() + if answer == u'': + log.debug(u'Under blank') + return default + if answer in yes: + log.debug(u'Must be true') + return True + elif answer in no: + log.debug(u'Must be false') + return False + else: + sys.stdout.write(u'Please answer yes or no only!\n\n') + sys.stdout.flush() + answer = None + six.moves.input(u'Press enter to continue') + sys.stdout.write('\n\n\n\n\n') + sys.stdout.flush() + + +def get_correct_answer(question, default=None, required=False, + answer=None, is_answer_correct=None): + u"""Ask user a question and confirm answer + + Args: + + question (str): Question to ask user + + default (str): Default answer if no input from user + + required (str): Require user to input answer + + answer (str): Used for testing + + is_answer_correct (str): Used for testing + """ + while 1: + if default is None: + msg = u' - No Default Available' + else: + msg = (u'\n[DEFAULT] -> {}\nPress Enter To ' + u'Use Default'.format(default)) + prompt = question + msg + u'\n--> ' + if answer is None: + answer = six.moves.input(prompt) + if answer == '' and required and default is not None: + print(u'You have to enter a value\n\n') + six.moves.input(u'Press enter to continue') + print(u'\n\n') + answer = None + continue + if answer == u'' and default is not None: + answer = default + _ans = ask_yes_no(u'You entered {}, is this ' + u'correct?'.format(answer), + answer=is_answer_correct) + if _ans: + return answer + else: + answer = None diff --git a/venv/Lib/site-packages/menus/__init__.py b/venv/Lib/site-packages/menus/__init__.py new file mode 100644 index 00000000..cdff786e --- /dev/null +++ b/venv/Lib/site-packages/menus/__init__.py @@ -0,0 +1,37 @@ +# The MIT License (MIT) +# +# Copyright (c) 2016 Digital Sapphire +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +import logging + +from menus.engine import Engine +from menus.exceptions import MenusError +from menus.menu import BaseMenu + + +__all__ = ['BaseMenu', 'Engine', 'MenusError'] + + +log = logging.getLogger() + + +from ._version import get_versions # noqa +__version__ = get_versions()['version'] +del get_versions diff --git a/venv/Lib/site-packages/menus/_version.py b/venv/Lib/site-packages/menus/_version.py new file mode 100644 index 00000000..6def6439 --- /dev/null +++ b/venv/Lib/site-packages/menus/_version.py @@ -0,0 +1,21 @@ + +# This file was generated by 'versioneer.py' (0.15) from +# revision-control system data, or from the parent directory name of an +# unpacked source archive. Distribution tarballs contain a pre-generated copy +# of this file. + +import json +import sys + +version_json = ''' +{ + "dirty": false, + "error": null, + "full-revisionid": "4dd50cb49a508b57d56a4d512ef40da67b01ab77", + "version": "0.2.0" +} +''' # END VERSION_JSON + + +def get_versions(): + return json.loads(version_json) diff --git a/venv/Lib/site-packages/menus/engine.py b/venv/Lib/site-packages/menus/engine.py new file mode 100644 index 00000000..1b0742a0 --- /dev/null +++ b/venv/Lib/site-packages/menus/engine.py @@ -0,0 +1,96 @@ +# -------------------------------------------------------------------------- +# Copyright (c) 2016 Digital Sapphire +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject to the +# following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +# ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +# TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +# SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +# OR OTHER DEALINGS IN THE SOFTWARE. +# -------------------------------------------------------------------------- +import logging +import sys + +from menus.example import load_example_menus +from menus.exceptions import MenusError +from menus.menu import BaseMenu, MainMenu +from menus.utils import check_commands_else_raise + + +log = logging.getLogger(__name__) + + +# Ensure that a custom menus subclasses BaseMenu +def check_mro(c): + if issubclass(c.__class__, BaseMenu) is False: + raise MenusError('Not a sublcass of BaseMenu \n\nclass ' + '{}'.format(c.__class__.__name__), + expected=True) + return True + + +class Engine(object): + + def __init__(self, app_name=None, menus=None, example=False): + # Name used in every menu header + if app_name is None: + app_name = 'ACME' + + # Create initial commands for main menu + sub_menus = [] + + # Adding submenus + if example is True: + sub_menus += load_example_menus() + else: + if menus is not None: + for m in menus: + check_mro(m) + sub_menus += menus + + # Commands with app_name added to it + new_sub_menus = [] + for sub in sub_menus: + # Adding the app name to the menu + sub.app_name = app_name + sub.commands.append(('Main Menu', getattr(sub, 'done'))) + + # Quick hack to add users class name as menu option + # only for main menu + new_sub_menu = (sub.menu_name, sub) + new_sub_menus.append(new_sub_menu) + + new_sub_menus.append(('Quit', self.quit)) + + # Sanatisation checks on passed commands + check_commands_else_raise(new_sub_menus) + + # Initilazie main menu with submenus + self.main = MainMenu(new_sub_menus) + + # Adding the app name to the main menu + self.main.app_name = app_name + + # Starts event loop + def start(self): # pragma: no cover + while 1: + start = self.main.display() + start() + + def quit(self): # pragma: no cover + log.debug('Quitting') + sys.exit(0) diff --git a/venv/Lib/site-packages/menus/example.py b/venv/Lib/site-packages/menus/example.py new file mode 100644 index 00000000..31df72df --- /dev/null +++ b/venv/Lib/site-packages/menus/example.py @@ -0,0 +1,98 @@ +# -------------------------------------------------------------------------- +# Copyright (c) 2016 Digital Sapphire +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject to the +# following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +# ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +# TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +# SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +# OR OTHER DEALINGS IN THE SOFTWARE. +# -------------------------------------------------------------------------- +import logging + +from menus.menu import BaseMenu + + +log = logging.getLogger(__name__) + + +class Cool(BaseMenu): + + def __init__(self): + # An option is a tuple which consists of ('Display Name', function) + commands = [('Speak', self.speak)] + super(Cool, self).__init__(commands=commands) + + def speak(self): + # Used to nicely display a message towards + # the middle of the screen + self.display_msg('Cool is speaking') + + # Will pause for 3 seconds + self.pause(seconds=3) + + # Used to return to Cool Menu. If omitted + # the user will be returned to the Main Menu + self() + + +class Hot(BaseMenu): + + def __init__(self): + # An option is a tuple which consists of ('Display Name', function) + commands = [('Speak', self.speak)] + super(Hot, self).__init__(commands=commands, menu_name='Really Hot') + + def speak(self): + # Used to nicely display a message towards + # the middle of the screen + self.display_msg("It's getting hot in here!") + + # Will pause for 3 seconds + self.pause(seconds=3) + + # Used to return to Cool Menu. If omitted + # the user will be returned to the Main Menu + self() + + +class Keys(BaseMenu): + + def __init__(self): + # An option is a tuple which consists of ('Display Name', function) + commands = [('Show Public Key', self.show_public_key)] + + super(Keys, self).__init__(commands=commands) + + def show_public_key(self): + log.debug('Show public key') + + # Used to nicely display a message towards + # the middle of the screen + self.display_msg('thdkalfjl;da;ksfkda;fdkj') + + # Will prompt user to press enter to continue + self.pause(enter_to_continue=True) + + # Used to return to Cool Menu. If omitted + # the user will be returned to the Main Menu + self() + + +# List of menus to be used when user initializes Engine(example=True) +def load_example_menus(): + return [Cool(), Hot(), Keys()] diff --git a/venv/Lib/site-packages/menus/exceptions.py b/venv/Lib/site-packages/menus/exceptions.py new file mode 100644 index 00000000..9d7c003e --- /dev/null +++ b/venv/Lib/site-packages/menus/exceptions.py @@ -0,0 +1,69 @@ +# -------------------------------------------------------------------------- +# Copyright (c) 2016 Digital Sapphire +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject to the +# following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +# ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +# TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +# SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +# OR OTHER DEALINGS IN THE SOFTWARE. +# -------------------------------------------------------------------------- +from __future__ import unicode_literals + +import sys +import traceback + + +class STDError(Exception): + """Extends exceptions to show added message if error isn't expected. + + Args: + + msg (str): error message + + Kwargs: + + tb (obj): is the original traceback so that it can be printed. + + expected (bool): + + Meaning: + + True - Report issue msg not shown + + False - Report issue msg shown + """ + def __init__(self, msg, tb=None, expected=False): + if not expected: + msg = msg + ('; please report this issue on https://git' + 'hub.com/JMSwag/Menus') + msg = '\n\n' + msg + super(STDError, self).__init__(msg) + + self.traceback = tb + self.exc_info = sys.exc_info() # preserve original exception + + def format_traceback(self): + if self.traceback is None: + return None + return ''.join(traceback.format_tb(self.traceback)) + + +class MenusError(STDError): + """Raised for Archiver exceptions""" + def __init__(self, *args, **kwargs): + super(MenusError, self).__init__(*args, **kwargs) diff --git a/venv/Lib/site-packages/menus/menu.py b/venv/Lib/site-packages/menus/menu.py new file mode 100644 index 00000000..da2c6152 --- /dev/null +++ b/venv/Lib/site-packages/menus/menu.py @@ -0,0 +1,206 @@ +# -------------------------------------------------------------------------- +# Copyright (c) 2016 Digital Sapphire +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject to the +# following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +# ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +# TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +# SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +# OR OTHER DEALINGS IN THE SOFTWARE. +# -------------------------------------------------------------------------- +from __future__ import print_function +import logging +import os +import time +import warnings + +from dsdev_utils.terminal import (ask_yes_no, + get_correct_answer, + get_terminal_size) +import six + +from menus.exceptions import MenusError +from menus.utils import clear_screen_cmd, Getch + +log = logging.getLogger(__name__) + + +class BaseMenu(object): + + def __init__(self, **kwargs): + # Used to display the apps name on all menu headers + self.app_name = None + + # The custom menu name + self.menu_name = kwargs.get('menu_name') + + # If we do not have a custom menu + # name then use the class name + if self.menu_name is None: + self.menu_name = self.__class__.__name__ + + # A message to display when this menus loads + self.message = kwargs.get('message') + + # User commands for this menu + self._commands = kwargs.get('commands', []) + + # ToDo: Remove in v1.0 + # User commands for this menu + options = kwargs.get('options', []) + if len(options) > 0: + if len(self._commands) > 0: + raise MenusError('Cannot mix old & new API. Use ' + 'commands kwarg only.') + + warn_msg = ('BaseMenu(options=[]) is deprecated, ' + 'user BaseMenu(commands=[]') + warnings.warn(warn_msg) + self._commands += options + # End ToDo + + @property + def commands(self): + return self._commands + + def __call__(self): + x = self.display() + x() + + def display(self): + self._display_menu_header() + self.display_msg(self.message) + return self._menu_options(self._commands) + + def pause(self, seconds=5, enter_to_continue=False): + if not isinstance(enter_to_continue, bool): + raise MenusError('enter_to_continue must be boolean', + expected=True) + if not isinstance(seconds, six.integer_types): + raise MenusError('seconds must be integer', expected=True) + + if enter_to_continue is True: + self.display_msg('Press enter to quit') + six.moves.input() + else: + time.sleep(seconds) + return True + + # Prompt the user with a question & only accept + # yes or no answers + def ask_yes_no(self, question, default): + return ask_yes_no(question, default) + + # Promot the user with a question & confirm it's correct. + # If required is True, user won't be able to enter a blank answer + def get_correct_answer(self, question, default, required=False): + return get_correct_answer(question, default, required) + + # Display a message centered on the screen. + def display_msg(self, message=None): + self._display_msg(message) + + def done(self): + pass + + # ToDo: Remove in v1.0 + def get_correct_action(self, question, default, required): + return get_correct_answer(question, default, required) + # End ToDo + + # Takes a string and adds it to the menu header along side + # the app name. + def _display_menu_header(self): + window_size = get_terminal_size()[0] + + # Adding some styling to the header + def add_style(): + top = '*' * window_size + '\n' + bottom = '\n' + '*' * window_size + '\n' + + header = self.app_name + ' - ' + self.menu_name + header = header.center(window_size) + msg = top + header + bottom + return msg + + os.system(clear_screen_cmd) + print(add_style()) + + def _display_msg(self, message): + window_size = get_terminal_size()[0] + if message is None: + return '' + + if not isinstance(message, six.string_types): + log.warning('Did not pass str') + return '' + + # Home grown word wrap + def format_msg(): + formatted = [] + finished = ['\n'] + count = 0 + words = message.split(' ') + for w in words: + w = w + ' ' + if count + len(w) > window_size / 2: + finished.append(''.join(formatted).center(window_size)) + finished.append('\n') + count = len(w) + # Starting a new line. + formatted = [] + formatted.append(w) + else: + formatted.append(w) + count += len(w) + finished.append(''.join(formatted).center(window_size)) + finished.append('\n') + return ''.join(finished) + print(format_msg()) + + # Takes a list of tuples(menu_name, call_back) adds menu numbers + # then prints menu to screen. + # Gets input from user, then returns the callback + def _menu_options(self, commands=None): + + def add_options(): + getch = Getch() + menu = [] + count = 1 + for s in commands: + item = '{}. {}\n'.format(count, s[0]) + menu.append(item) + count += 1 + print(''.join(menu)) + answers = [] + for a in six.moves.range(1, len(menu) + 1): + answers.append(str(a)) + while 1: + ans = getch() + if ans in answers: + break + else: + log.debug('Not an acceptable answer!') + return commands[int(ans) - 1][1] + return add_options() + + +# This is used as the initial Menu +class MainMenu(BaseMenu): + + def __init__(self, commands): + super(MainMenu, self).__init__(commands=commands) diff --git a/venv/Lib/site-packages/menus/utils.py b/venv/Lib/site-packages/menus/utils.py new file mode 100644 index 00000000..0a18b8cd --- /dev/null +++ b/venv/Lib/site-packages/menus/utils.py @@ -0,0 +1,118 @@ +# The MIT License (MIT) +# +# Copyright (c) 2016 Digital Sapphire +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +import logging +import sys + +import six + +from menus.exceptions import MenusError + +# Preventing import errors on non windows +# platforms +if sys.platform == 'win32': + import msvcrt +else: + import termios + import tty + + +log = logging.getLogger(__name__) + +if sys.platform == 'win32': + clear_screen_cmd = 'cls' +else: + clear_screen_cmd = 'clear' + + +def check_commands_else_raise(options): + # We need a least one thing to display + # Using this as a teaching aid. Also making the use of + # Engine(example=Ture) very explicit + if len(options) == 0: + msg = ('You must pass a menus object or initilize ' + 'like -> Engine(example=True)') + raise MenusError(msg, expected=True) + + # We need a list or tuple to loop through + if not isinstance(options, list): + if not isinstance(options, tuple): + msg = ('You must pass a list or tuple to menus.') + raise MenusError(msg, expected=True) + + if len(options) > 9: + msg = ('Cannot have more then 8 options per menu') + raise MenusError(msg, expected=True) + + for o in options: + # Ensuring each item in list/tuple is a tuple + if not isinstance(o, tuple): + raise MenusError('Item must be tuple: {}'.format(o), expected=True) + + if len(o) != 2: + raise MenusError('Invalid number of tuple ' + 'items:\n\n{}'.format(o)) + # Ensure index 0 is a str + if not isinstance(o[0], six.string_types): + msg = 'Menus are passed as [("Menu Name", MenuObject())]' + raise MenusError(msg) + + return True + + +# Gets a single character form standard input. Does not echo to the screen +class Getch(object): + + def __init__(self): + if sys.platform == 'win32': + self.impl = GetchWindows() + else: + self.impl = GetchUnix() + + def __call__(self): + return self.impl() + + +class GetchUnix(object): + def __init__(self): + # Not sure if these imports are required here + # import tty, sys + pass + + def __call__(self): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(sys.stdin.fileno()) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + +class GetchWindows(object): + def __init__(self): + # Not sure if this import is required + # import msvcrt + pass + + def __call__(self): + return msvcrt.getch() diff --git a/venv/Lib/site-packages/site/__init__.py b/venv/Lib/site-packages/site/__init__.py new file mode 100644 index 00000000..e69de29b