fb-tools 2.6.3__tar.gz → 2.7.0__tar.gz

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 (69) hide show
  1. {fb_tools-2.6.3/lib/fb_tools.egg-info → fb_tools-2.7.0}/PKG-INFO +1 -1
  2. fb_tools-2.7.0/bin/show-spinner +78 -0
  3. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/__init__.py +1 -1
  4. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/ddns/__init__.py +40 -2
  5. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/errors.py +8 -1
  6. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/handling_obj.py +28 -11
  7. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/local_version.py +1 -1
  8. fb_tools-2.7.0/lib/fb_tools/show_spinner_app.py +300 -0
  9. {fb_tools-2.6.3 → fb_tools-2.7.0/lib/fb_tools.egg-info}/PKG-INFO +1 -1
  10. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools.egg-info/SOURCES.txt +2 -0
  11. fb_tools-2.7.0/locale/de/LC_MESSAGES/fb_tools.mo +0 -0
  12. {fb_tools-2.6.3 → fb_tools-2.7.0}/locale/de/LC_MESSAGES/fb_tools.po +176 -105
  13. fb_tools-2.7.0/locale/en/LC_MESSAGES/fb_tools.mo +0 -0
  14. {fb_tools-2.6.3 → fb_tools-2.7.0}/locale/en/LC_MESSAGES/fb_tools.po +164 -104
  15. {fb_tools-2.6.3 → fb_tools-2.7.0}/locale/fb_tools.pot +163 -103
  16. {fb_tools-2.6.3 → fb_tools-2.7.0}/test/test_20_handling_object.py +48 -1
  17. fb_tools-2.6.3/locale/de/LC_MESSAGES/fb_tools.mo +0 -0
  18. fb_tools-2.6.3/locale/en/LC_MESSAGES/fb_tools.mo +0 -0
  19. {fb_tools-2.6.3 → fb_tools-2.7.0}/LICENSE +0 -0
  20. {fb_tools-2.6.3 → fb_tools-2.7.0}/MANIFEST.in +0 -0
  21. {fb_tools-2.6.3 → fb_tools-2.7.0}/README.md +0 -0
  22. {fb_tools-2.6.3 → fb_tools-2.7.0}/bin/get-file-to-remove +0 -0
  23. {fb_tools-2.6.3 → fb_tools-2.7.0}/bin/myip +0 -0
  24. {fb_tools-2.6.3 → fb_tools-2.7.0}/bin/update-ddns +0 -0
  25. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/app.py +0 -0
  26. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/argparse_actions.py +0 -0
  27. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/cfg_app.py +0 -0
  28. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/colcts.py +0 -0
  29. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/collections.py +0 -0
  30. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/common.py +0 -0
  31. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/config.py +0 -0
  32. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/ddns/config.py +0 -0
  33. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/ddns/errors.py +0 -0
  34. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/ddns/myip_app.py +0 -0
  35. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/ddns/update_app.py +0 -0
  36. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/get_file_rm_app.py +0 -0
  37. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/handler/__init__.py +0 -0
  38. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/handler/lock.py +0 -0
  39. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/mailaddress.py +0 -0
  40. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/merge.py +0 -0
  41. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/multi_config/__init__.py +0 -0
  42. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/multi_config/files.py +0 -0
  43. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/multi_config/inits.py +0 -0
  44. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/multi_config/read.py +0 -0
  45. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/obj.py +0 -0
  46. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/pidfile.py +0 -0
  47. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/spinner.py +0 -0
  48. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools/xlate.py +0 -0
  49. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools.egg-info/dependency_links.txt +0 -0
  50. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools.egg-info/requires.txt +0 -0
  51. {fb_tools-2.6.3 → fb_tools-2.7.0}/lib/fb_tools.egg-info/top_level.txt +0 -0
  52. {fb_tools-2.6.3 → fb_tools-2.7.0}/setup.cfg +0 -0
  53. {fb_tools-2.6.3 → fb_tools-2.7.0}/setup.py +0 -0
  54. {fb_tools-2.6.3 → fb_tools-2.7.0}/test/call_script.sh +0 -0
  55. {fb_tools-2.6.3 → fb_tools-2.7.0}/test/call_sleep.sh +0 -0
  56. {fb_tools-2.6.3 → fb_tools-2.7.0}/test/do_sleep +0 -0
  57. {fb_tools-2.6.3 → fb_tools-2.7.0}/test/general.py +0 -0
  58. {fb_tools-2.6.3 → fb_tools-2.7.0}/test/test_00_errors.py +0 -0
  59. {fb_tools-2.6.3 → fb_tools-2.7.0}/test/test_05_common.py +0 -0
  60. {fb_tools-2.6.3 → fb_tools-2.7.0}/test/test_10_base_object.py +0 -0
  61. {fb_tools-2.6.3 → fb_tools-2.7.0}/test/test_13_colcts.py +0 -0
  62. {fb_tools-2.6.3 → fb_tools-2.7.0}/test/test_15_mailaddress.py +0 -0
  63. {fb_tools-2.6.3 → fb_tools-2.7.0}/test/test_17_multicfg.py +0 -0
  64. {fb_tools-2.6.3 → fb_tools-2.7.0}/test/test_18_pidfile.py +0 -0
  65. {fb_tools-2.6.3 → fb_tools-2.7.0}/test/test_25_ddns_update_status.py +0 -0
  66. {fb_tools-2.6.3 → fb_tools-2.7.0}/test/test_30_base_handler.py +0 -0
  67. {fb_tools-2.6.3 → fb_tools-2.7.0}/test/test_33_lock.py +0 -0
  68. {fb_tools-2.6.3 → fb_tools-2.7.0}/test/test_40_app_modules.py +0 -0
  69. {fb_tools-2.6.3 → fb_tools-2.7.0}/test/test_50_spinner.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fb_tools
3
- Version: 2.6.3
3
+ Version: 2.7.0
4
4
  Summary: Modules for common used objects, error classes and methods.
5
5
  Home-page: https://github.com/fbrehm/python_fb_tools
6
6
  Author: Frank Brehm
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ @summary: Print a newline separated list of files generated from file globbing patterns.
5
+
6
+ @author: Frank Brehm
7
+ @contact: frank.brehm@pixelpark.com
8
+ @copyright: © 2024 by Frank Brehm, Berlin
9
+ """
10
+ from __future__ import print_function
11
+
12
+ # Standard modules
13
+ import locale
14
+ import sys
15
+
16
+ try:
17
+ from pathlib import Path
18
+ except ImportError:
19
+ from pathlib2 import Path
20
+
21
+ __exp_py_version_major__ = 3
22
+ __min_py_version_minor__ = 6
23
+
24
+ if sys.version_info[0] != __exp_py_version_major__:
25
+ print('This script is intended to use with Python {}.'.format(
26
+ __exp_py_version_major__), file=sys.stderr)
27
+ print('You are using Python: {0}.{1}.{2}-{3}-{4}.'.format(
28
+ *sys.version_info) + '\n', file=sys.stderr)
29
+ sys.exit(1)
30
+
31
+ if sys.version_info[1] < __min_py_version_minor__:
32
+ print('A minimal Python version of {maj}.{min} is necessary to execute this script.'.format(
33
+ maj=__exp_py_version_major__, min=__min_py_version_minor__), file=sys.stderr)
34
+ print('You are using Python: {0}.{1}.{2}-{3}-{4}.'.format(
35
+ *sys.version_info) + '\n', file=sys.stderr)
36
+ sys.exit(1)
37
+
38
+ my_path = Path(__file__)
39
+ my_real_path = my_path.resolve()
40
+ bin_path = my_real_path.parent
41
+ base_dir = bin_path.parent
42
+ lib_dir = base_dir.joinpath('lib')
43
+ module_dir = lib_dir.joinpath('fb_tools')
44
+
45
+ # print("My path: {!r}".format(str(my_path)))
46
+ # print("My real path: {!r}".format(str(my_real_path)))
47
+ # print("Bin path: {!r}".format(str(bin_path)))
48
+ # print("Base dir: {!r}".format(str(base_dir)))
49
+ # print("Lib dir: {!r}".format(str(lib_dir)))
50
+ # print("Module dir: {!r}".format(str(module_dir)))
51
+
52
+ if module_dir.exists():
53
+ sys.path.insert(0, str(lib_dir))
54
+
55
+ # own modules:
56
+ from fb_tools.show_spinner_app import ShowSpinnerApplication
57
+ from fb_tools.xlate import XLATOR
58
+
59
+ _ = XLATOR.gettext
60
+
61
+ __author__ = 'Frank Brehm <frank.brehm@pixelpark.com>'
62
+ __copyright__ = '(C) 2024 by Frank Brehm, Pixelpark GmbH, Berlin'
63
+
64
+ appname = my_path.name
65
+
66
+ locale.setlocale(locale.LC_ALL, '')
67
+
68
+ app = ShowSpinnerApplication(appname=appname, base_dir=base_dir)
69
+ app.initialized = True
70
+
71
+ if app.verbose > 2:
72
+ print(_('{c}-Object:\n{a}').format(c=app.__class__.__name__, a=app), file=sys.stderr)
73
+
74
+ app()
75
+
76
+ sys.exit(0)
77
+
78
+ # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
@@ -22,6 +22,6 @@ DEFAULT_TERMINAL_HEIGHT = 40
22
22
  from .mailaddress import MailAddress, MailAddressList, QualifiedMailAddress # noqa: F401
23
23
  from .multi_config import BaseMultiConfig # noqa: F401
24
24
 
25
- __version__ = '2.6.3'
25
+ __version__ = '2.7.0'
26
26
 
27
27
  # vim: ts=4 et list
@@ -46,10 +46,11 @@ from .. import __version__ as GLOBAL_VERSION
46
46
  from ..argparse_actions import DirectoryOptionAction
47
47
  from ..cfg_app import FbConfigApplication
48
48
  from ..common import pp
49
+ from ..common import to_bool
49
50
  from ..errors import IoTimeoutError
50
51
  from ..xlate import XLATOR, format_list
51
52
 
52
- __version__ = '2.2.3'
53
+ __version__ = '2.3.0'
53
54
  LOG = logging.getLogger(__name__)
54
55
 
55
56
  _ = XLATOR.gettext
@@ -65,6 +66,8 @@ class BaseDdnsApplication(FbConfigApplication):
65
66
  work_directory_must_exists = False
66
67
  work_directory_must_be_writeable = False
67
68
 
69
+ default_verify_server_cert = True
70
+
68
71
  # -------------------------------------------------------------------------
69
72
  def __init__(
70
73
  self, appname=None, verbose=0, version=GLOBAL_VERSION, base_dir=None,
@@ -76,6 +79,7 @@ class BaseDdnsApplication(FbConfigApplication):
76
79
  description = _('This is a base DDNS related application.')
77
80
 
78
81
  self._user_agent = '{}/{}'.format(self.library_name, GLOBAL_VERSION)
82
+ self._verify_server_cert = self.default_verify_server_cert
79
83
 
80
84
  super(BaseDdnsApplication, self).__init__(
81
85
  appname=appname, verbose=verbose, version=version, base_dir=base_dir,
@@ -99,6 +103,19 @@ class BaseDdnsApplication(FbConfigApplication):
99
103
  raise DdnsAppError(_('Invalid user agent {!r} given.').format(value))
100
104
  self._user_agent = str(value).strip()
101
105
 
106
+ # -----------------------------------------------------------
107
+ @property
108
+ def verify_server_cert(self):
109
+ """Return whether to verify the SSL certificate of the requested server."""
110
+ return self._verify_server_cert
111
+
112
+ @verify_server_cert.setter
113
+ def verify_server_cert(self, value):
114
+ if value is None:
115
+ self._verify_server_cert = self.default_verify_server_cert
116
+ return
117
+ self._verify_server_cert = to_bool(value)
118
+
102
119
  # -------------------------------------------------------------------------
103
120
  @property
104
121
  def has_dns_module(self):
@@ -119,6 +136,7 @@ class BaseDdnsApplication(FbConfigApplication):
119
136
  res = super(BaseDdnsApplication, self).as_dict(short=short)
120
137
  res['has_dns_module'] = self.has_dns_module
121
138
  res['user_agent'] = self.user_agent
139
+ res['verify_server_cert'] = self.verify_server_cert
122
140
 
123
141
  return res
124
142
 
@@ -176,6 +194,14 @@ class BaseDdnsApplication(FbConfigApplication):
176
194
  DdnsConfiguration.default_timeout),
177
195
  )
178
196
 
197
+ ddns_group.add_argument(
198
+ '-k', '--insecure', dest='insecure', action='store_true',
199
+ help=_(
200
+ 'By default, every secure connection {app} makes is verified to be secure before '
201
+ 'the request takes place. This option makes {app} skip the verification step and '
202
+ 'proceed without checking.').format(app=self.appname),
203
+ )
204
+
179
205
  super(BaseDdnsApplication, self).init_arg_parser()
180
206
 
181
207
  # -------------------------------------------------------------------------
@@ -225,6 +251,9 @@ class BaseDdnsApplication(FbConfigApplication):
225
251
  logging.getLogger('requests').setLevel(logging.WARNING)
226
252
  self.loglevel_requests_set = True
227
253
 
254
+ if self.args.insecure:
255
+ self.verify_server_cert = False
256
+
228
257
  self.initialized = True
229
258
 
230
259
  # -------------------------------------------------------------------------
@@ -251,6 +280,15 @@ class BaseDdnsApplication(FbConfigApplication):
251
280
  self, url, method='GET', data=None, headers=None, may_simulate=False,
252
281
  return_json=True):
253
282
  """Perform the underlying Web request."""
283
+ verify = None
284
+ if url.startswith('https://'):
285
+ if self.verify_server_cert:
286
+ verify = True
287
+ else:
288
+ verify = False
289
+ urllib3.disable_warnings()
290
+ LOG.debug(f'Verifying remote server certificate: {verify!r}.')
291
+
254
292
  if headers is None:
255
293
  headers = {}
256
294
 
@@ -282,7 +320,7 @@ class BaseDdnsApplication(FbConfigApplication):
282
320
 
283
321
  session = requests.Session()
284
322
  response = session.request(
285
- method, url, data=data, headers=headers, timeout=self.cfg.timeout)
323
+ method, url, data=data, headers=headers, timeout=self.cfg.timeout, verify=verify)
286
324
 
287
325
  except (
288
326
  socket.timeout, urllib3.exceptions.ConnectTimeoutError,
@@ -14,7 +14,7 @@ import signal
14
14
  # Own modules
15
15
  from .xlate import XLATOR
16
16
 
17
- __version__ = '2.3.1'
17
+ __version__ = '2.4.0'
18
18
 
19
19
  _ = XLATOR.gettext
20
20
  ngettext = XLATOR.ngettext
@@ -150,6 +150,13 @@ class AbortAppError(FbAppError):
150
150
  pass
151
151
 
152
152
 
153
+ # =============================================================================
154
+ class ExitAppError(FbAppError):
155
+ """Special exception class exiting the application."""
156
+
157
+ pass
158
+
159
+
153
160
  # =============================================================================
154
161
  class FbCfgAppError(FbAppError):
155
162
  """Generic exception class for all configured application classes."""
@@ -51,12 +51,17 @@ import six
51
51
  from . import DEFAULT_TERMINAL_HEIGHT, DEFAULT_TERMINAL_WIDTH
52
52
  from .common import caller_search_path, encode_or_bust, pp, to_bool, to_str
53
53
  from .common import indent, is_sequence
54
- from .errors import AbortAppError, TimeoutOnPromptError
55
- from .errors import InterruptError, IoTimeoutError, ReadTimeoutError, WriteTimeoutError
54
+ from .errors import AbortAppError
55
+ from .errors import ExitAppError
56
+ from .errors import InterruptError
57
+ from .errors import IoTimeoutError
58
+ from .errors import ReadTimeoutError
59
+ from .errors import TimeoutOnPromptError
60
+ from .errors import WriteTimeoutError
56
61
  from .obj import FbBaseObject
57
62
  from .xlate import XLATOR, format_list
58
63
 
59
- __version__ = '2.2.5'
64
+ __version__ = '2.3.0'
60
65
  LOG = logging.getLogger(__name__)
61
66
 
62
67
  _ = XLATOR.gettext
@@ -941,7 +946,9 @@ class HandlingObject(FbBaseObject):
941
946
  return
942
947
 
943
948
  # -------------------------------------------------------------------------
944
- def get_password(self, first_prompt=None, second_prompt=None, may_empty=True, repeat=True):
949
+ def get_password(
950
+ self, first_prompt=None, second_prompt=None, may_empty=True, repeat=True,
951
+ raise_on_exit=False):
945
952
  """
946
953
  Ask the user for a password on the console.
947
954
 
@@ -959,6 +966,9 @@ class HandlingObject(FbBaseObject):
959
966
  @param repeat: Asking for the password a second time, which must be equal
960
967
  to the first given password.
961
968
  @type repeat: bool
969
+ @param raise_on_exit: raising an ExitAppError instead of exiting the application
970
+ in different situations
971
+ @type raise_on_exit: bool
962
972
 
963
973
  @return: The entered password
964
974
  @rtype: str
@@ -977,10 +987,11 @@ class HandlingObject(FbBaseObject):
977
987
 
978
988
  if not self.quiet:
979
989
  print()
980
- ret_passwd = self._get_password(first_prompt, may_empty)
990
+ ret_passwd = self._get_password(first_prompt, may_empty, raise_on_exit=raise_on_exit)
981
991
  if ret_passwd:
982
992
  if repeat:
983
- second_passwd = self._get_password(second_prompt, may_empty=False)
993
+ second_passwd = self._get_password(
994
+ second_prompt, may_empty=False, raise_on_exit=raise_on_exit)
984
995
  if ret_passwd != second_passwd:
985
996
  msg = _('The entered passwords does not match.')
986
997
  LOG.error(msg)
@@ -990,7 +1001,7 @@ class HandlingObject(FbBaseObject):
990
1001
  return ret_passwd
991
1002
 
992
1003
  # -------------------------------------------------------------------------
993
- def _get_password(self, prompt, may_empty=True):
1004
+ def _get_password(self, prompt, may_empty=True, raise_on_exit=False):
994
1005
 
995
1006
  def passwd_alarm_caller(signum, sigframe):
996
1007
  raise TimeoutOnPromptError(self.prompt_timeout)
@@ -1021,13 +1032,19 @@ class HandlingObject(FbBaseObject):
1021
1032
 
1022
1033
  except (TimeoutOnPromptError, AbortAppError) as e:
1023
1034
  msg = _('Got a {}:').format(e.__class__.__name__) + ' ' + str(e)
1024
- LOG.error(msg)
1025
- sys.exit(10)
1035
+ if raise_on_exit:
1036
+ raise ExitAppError(msg)
1037
+ else:
1038
+ LOG.error(msg)
1039
+ sys.exit(10)
1026
1040
 
1027
1041
  except KeyboardInterrupt:
1028
1042
  msg = _('Got a {}:').format('KeyboardInterrupt') + ' ' + msg_intr
1029
- LOG.error(msg)
1030
- sys.exit(10)
1043
+ if raise_on_exit:
1044
+ raise ExitAppError(msg)
1045
+ else:
1046
+ LOG.error(msg)
1047
+ sys.exit(10)
1031
1048
 
1032
1049
  finally:
1033
1050
  signal.alarm(0)
@@ -11,7 +11,7 @@
11
11
  __author__ = 'Frank Brehm <frank@brehm-online.com>'
12
12
  __copyright__ = '(C) 2024 by Frank Brehm, Berlin'
13
13
  __contact__ = 'frank@brehm-online.com'
14
- __version__ = '2.6.3'
14
+ __version__ = '2.7.0'
15
15
  __license__ = 'LGPL3+'
16
16
 
17
17
  # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
@@ -0,0 +1,300 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ @summary: The module for the show-spinner application class.
5
+
6
+ @author: Frank Brehm
7
+ @contact: frank.brehm@pixelpark.com
8
+ @copyright: © 2024 by Frank Brehm, Berlin
9
+ """
10
+ from __future__ import absolute_import
11
+
12
+ # Standard modules
13
+ import copy
14
+ import logging
15
+ import random
16
+ import signal
17
+ import sys
18
+ import time
19
+
20
+ # Third party modules
21
+
22
+ # Own modules
23
+ from . import __version__ as __global_version__
24
+ from .app import BaseApplication
25
+ from .app import SIGNAL_NAMES
26
+ from .spinner import CycleList
27
+ from .spinner import Spinner
28
+ from .xlate import XLATOR
29
+
30
+ __version__ = '0.2.0'
31
+ LOG = logging.getLogger(__name__)
32
+
33
+ _ = XLATOR.gettext
34
+ ngettext = XLATOR.ngettext
35
+
36
+ # =============================================================================
37
+ class ShowSpinnerApplication(BaseApplication):
38
+ """Class for the show-spinner application object."""
39
+
40
+ default_show_time = 6
41
+ default_spinner = 'random'
42
+ default_prompt = _('Waiting ... ')
43
+
44
+ # -------------------------------------------------------------------------
45
+ def __init__(
46
+ self, verbose=0, version=__global_version__, *arg, **kwargs):
47
+ """Initialise of the show-spinnerapplication object."""
48
+ desc = _(
49
+ 'Shows one or more spinners, and their names, if multiple spinners should be shown. '
50
+ 'If no spinner is given, a random spinner will be displayed.')
51
+
52
+ self._show_time = None
53
+ self._prompt = self.default_prompt
54
+
55
+ self.spinners = []
56
+ self.all_spinners = sorted(CycleList.keys(), key=str.lower)
57
+
58
+ super(ShowSpinnerApplication, self).__init__(
59
+ description=desc,
60
+ verbose=verbose,
61
+ version=version,
62
+ *arg, **kwargs
63
+ )
64
+
65
+ self.initialized = True
66
+
67
+ # -----------------------------------------------------------
68
+ @property
69
+ def show_time(self):
70
+ """Give the number of seconds for displaying each spinner."""
71
+ return self._show_time
72
+
73
+ @show_time.setter
74
+ def show_time(self, value):
75
+ if value is None:
76
+ self._show_time = None
77
+ return
78
+
79
+ v = float(value)
80
+ if v < 0:
81
+ msg = _(
82
+ 'A negative time for showing the spinner {v!r} is not allowed.').format(
83
+ value)
84
+ raise ValueError(msg)
85
+
86
+ if v == 0:
87
+ self._show_time = None
88
+ else:
89
+ self._show_time = v
90
+
91
+ # -----------------------------------------------------------
92
+ @property
93
+ def prompt(self):
94
+ """Give the prompt displayed before the spinner."""
95
+ return self._prompt
96
+
97
+ @prompt.setter
98
+ def prompt(self, value):
99
+ if value is None:
100
+ self._prompt = ''
101
+ return
102
+
103
+ self._prompt = str(value)
104
+
105
+ # -------------------------------------------------------------------------
106
+ def as_dict(self, short=True):
107
+ """
108
+ Transform the elements of the object into a dict.
109
+
110
+ @param short: don't include local properties in resulting dict.
111
+ @type short: bool
112
+
113
+ @return: structure as dict
114
+ @rtype: dict
115
+ """
116
+ res = super(ShowSpinnerApplication, self).as_dict(short=short)
117
+
118
+ res['prompt'] = self.prompt
119
+ res['show_time'] = self.show_time
120
+
121
+ return res
122
+
123
+ # -------------------------------------------------------------------------
124
+ def init_arg_parser(self):
125
+ """Initialise the argument parser."""
126
+ super(ShowSpinnerApplication, self).init_arg_parser()
127
+
128
+ app_group = self.arg_parser.add_argument_group(_('Options for {}').format(self.appname))
129
+
130
+ app_group.add_argument(
131
+ '-P', '--prompt', dest='prompt',
132
+ help=_(
133
+ 'The prompt displayed before the spinner, if only one spinner should be shown. '
134
+ 'Default: {!r}.').format(self.default_prompt),
135
+ )
136
+
137
+ app_group.add_argument(
138
+ '-t', '--time', dest='time', metavar=_('SECONDS'), type=float,
139
+ help=_(
140
+ 'The time in seconds for displaying each spinner.').format(
141
+ self.default_show_time),
142
+ )
143
+
144
+ app_group.add_argument(
145
+ 'spinnerei', metavar=_('SPINNER'), type=str, nargs='*',
146
+ help=_(
147
+ 'The spinners, which should be displayed. If not given, a random spinner will be '
148
+ 'displayed, which is the same as giving {rand!r} as the name of the spinner. '
149
+ 'If giving {list!r}, a simple list of all available spinners will be shown, '
150
+ 'without displaying them. If giving {all!r}, all available spinners will be '
151
+ 'shown.').format(rand='random', list='list', all='all'),
152
+ )
153
+
154
+ # -------------------------------------------------------------------------
155
+ def perform_arg_parser(self):
156
+ """Parse the command line options."""
157
+ if self.args.prompt is not None:
158
+ self.prompt = self.args.prompt
159
+
160
+ if self.args.time is not None:
161
+ try:
162
+ self.show_time = self.args.time
163
+ except (ValueError, TypeError) as e:
164
+ LOG.error(str(e))
165
+ self.arg_parser.print_usage(sys.stdout)
166
+ self.exit(1)
167
+
168
+ all_spinners = ['random'] + self.all_spinners
169
+
170
+ if self.args.spinnerei:
171
+ for spinner in self.args.spinnerei:
172
+ if spinner == 'all':
173
+ if len(self.spinners):
174
+ msg = _('You may give {!r} only as the only spinner.').format('all')
175
+ LOG.error(msg)
176
+ self.arg_parser.print_usage(sys.stdout)
177
+ self.exit(1)
178
+ self.spinners.append(spinner)
179
+ if not self.show_time:
180
+ self.show_time = self.default_show_time
181
+ continue
182
+
183
+ if spinner == 'list':
184
+ if len(self.spinners):
185
+ msg = _('You may give {!r} only as the only spinner.').format('list')
186
+ LOG.error(msg)
187
+ self.arg_parser.print_usage(sys.stdout)
188
+ self.exit(1)
189
+ self.spinners.append(spinner)
190
+ continue
191
+
192
+ if spinner not in all_spinners:
193
+ msg = _('Invalid spinner {!r} given.').format(spinner)
194
+ LOG.error(msg)
195
+ self.arg_parser.print_usage(sys.stdout)
196
+ self.exit(1)
197
+
198
+ self.spinners.append(spinner)
199
+ else:
200
+ self.spinners.append(self.default_spinner)
201
+
202
+ # -------------------------------------------------------------------------
203
+ def _run(self):
204
+ """Run the application."""
205
+ prompt = self.prompt
206
+
207
+ show_time = 1
208
+ if self.show_time:
209
+ show_time = self.show_time
210
+
211
+ if 'list' in self.spinners:
212
+ self.list_spinners()
213
+ self.exit(0)
214
+
215
+ if 'all' in self.spinners:
216
+ self.spinners = copy.copy(self.all_spinners)
217
+
218
+ if len(self.spinners) > 1 or ('random' in self.spinners and self.args.prompt is None):
219
+ prompt = _('Spinner {!r}: ')
220
+
221
+ # ---------------------
222
+ def _signal_handler(signum, frame):
223
+
224
+ signame = '{}'.format(signum)
225
+ msg = _('Got a signal {}.').format(signum)
226
+ if signum in SIGNAL_NAMES:
227
+ signame = SIGNAL_NAMES[signum]
228
+ msg = _('Got a signal {n!r} ({s}).').format(
229
+ n=signame, s=signum)
230
+ LOG.debug(msg)
231
+
232
+ if signum in (
233
+ signal.SIGHUP, signal.SIGINT, signal.SIGABRT,
234
+ signal.SIGTERM, signal.SIGKILL, signal.SIGQUIT):
235
+ print()
236
+ if self.verbose > 1:
237
+ LOG.info(_('Exit on signal {n!r} ({s}).').format(n=signame, s=signum))
238
+ self.exit(0)
239
+
240
+ # ------------------------
241
+ old_handlers = {}
242
+
243
+ if self.verbose > 2:
244
+ LOG.debug(_('Tweaking signal handlers.'))
245
+ for signum in (
246
+ signal.SIGHUP, signal.SIGINT, signal.SIGABRT,
247
+ signal.SIGTERM, signal.SIGQUIT):
248
+ if self.verbose > 3:
249
+ signame = SIGNAL_NAMES[signum]
250
+ LOG.debug(_('Setting signal handler for {n!r} ({s}).').format(
251
+ n=signame, s=signum))
252
+ old_handlers[signum] = signal.signal(signum, _signal_handler)
253
+
254
+ try:
255
+ for spinner_name in self.spinners:
256
+ if spinner_name == 'random':
257
+ spinner_name = self._get_random_spinner_name()
258
+ print(prompt.format(spinner_name), end='', flush=True)
259
+
260
+ with Spinner('', spinner_name):
261
+ if self.show_time:
262
+ time.sleep(show_time)
263
+ else:
264
+ while True:
265
+ time.sleep(show_time)
266
+ print()
267
+ finally:
268
+ if self.verbose > 2:
269
+ LOG.debug(_('Restoring original signal handlers.'))
270
+ for signum in old_handlers.keys():
271
+ signal.signal(signum, old_handlers[signum])
272
+
273
+ self.exit(0)
274
+
275
+ # -------------------------------------------------------------------------
276
+ def _get_random_spinner_name(self):
277
+
278
+ randomizer = random.SystemRandom()
279
+ return randomizer.choice(list(CycleList.keys()))
280
+
281
+ # -------------------------------------------------------------------------
282
+ def list_spinners(self):
283
+ """Print out a list of all available spinners."""
284
+ if self.verbose:
285
+ title = _('All available spinners:')
286
+ print(title)
287
+ print('-' * len(title))
288
+
289
+ for spinner_name in self.all_spinners:
290
+ print(spinner_name)
291
+
292
+
293
+ # =============================================================================
294
+ if __name__ == '__main__':
295
+
296
+ pass
297
+
298
+ # =============================================================================
299
+
300
+ # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fb_tools
3
- Version: 2.6.3
3
+ Version: 2.7.0
4
4
  Summary: Modules for common used objects, error classes and methods.
5
5
  Home-page: https://github.com/fbrehm/python_fb_tools
6
6
  Author: Frank Brehm
@@ -5,6 +5,7 @@ setup.cfg
5
5
  setup.py
6
6
  bin/get-file-to-remove
7
7
  bin/myip
8
+ bin/show-spinner
8
9
  bin/update-ddns
9
10
  lib/fb_tools/__init__.py
10
11
  lib/fb_tools/app.py
@@ -22,6 +23,7 @@ lib/fb_tools/mailaddress.py
22
23
  lib/fb_tools/merge.py
23
24
  lib/fb_tools/obj.py
24
25
  lib/fb_tools/pidfile.py
26
+ lib/fb_tools/show_spinner_app.py
25
27
  lib/fb_tools/spinner.py
26
28
  lib/fb_tools/xlate.py
27
29
  lib/fb_tools.egg-info/PKG-INFO