inspy-logger 3.0.dev5__tar.gz → 3.0.2.dev1__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 (22) hide show
  1. {inspy_logger-3.0.dev5 → inspy_logger-3.0.2.dev1}/PKG-INFO +1 -1
  2. inspy_logger-3.0.2.dev1/inspy_logger/Scripts/main.py +105 -0
  3. {inspy_logger-3.0.dev5 → inspy_logger-3.0.2.dev1}/inspy_logger/__about__.py +0 -13
  4. {inspy_logger-3.0.dev5 → inspy_logger-3.0.2.dev1}/inspy_logger/__init__.py +4 -0
  5. {inspy_logger-3.0.dev5 → inspy_logger-3.0.2.dev1}/inspy_logger/helpers/base_classes.py +10 -0
  6. inspy_logger-3.0.2.dev1/inspy_logger/helpers/debug/__init__.py +0 -0
  7. inspy_logger-3.0.2.dev1/inspy_logger/helpers/debug/environment.py +179 -0
  8. inspy_logger-3.0.2.dev1/inspy_logger/helpers/debug/system/__init__.py +0 -0
  9. inspy_logger-3.0.2.dev1/inspy_logger/helpers/debug/system/processor/__init__.py +64 -0
  10. inspy_logger-3.0.2.dev1/inspy_logger/helpers/debug/system/winx/__init__.py +25 -0
  11. {inspy_logger-3.0.dev5 → inspy_logger-3.0.2.dev1}/inspy_logger/helpers/network.py +5 -5
  12. inspy_logger-3.0.2.dev1/inspy_logger/helpers/units.py +57 -0
  13. inspy_logger-3.0.2.dev1/inspy_logger/version.py +240 -0
  14. {inspy_logger-3.0.dev5 → inspy_logger-3.0.2.dev1}/pyproject.toml +5 -1
  15. inspy_logger-3.0.dev5/inspy_logger/Scripts/main.py +0 -91
  16. inspy_logger-3.0.dev5/inspy_logger/helpers/cache.py +0 -140
  17. inspy_logger-3.0.dev5/inspy_logger/version.py +0 -287
  18. {inspy_logger-3.0.dev5 → inspy_logger-3.0.2.dev1}/LICENSE.md +0 -0
  19. {inspy_logger-3.0.dev5 → inspy_logger-3.0.2.dev1}/README.md +0 -0
  20. {inspy_logger-3.0.dev5 → inspy_logger-3.0.2.dev1}/inspy_logger/errors/__init__.py +0 -0
  21. {inspy_logger-3.0.dev5 → inspy_logger-3.0.2.dev1}/inspy_logger/helpers/__init__.py +0 -0
  22. {inspy_logger-3.0.dev5 → inspy_logger-3.0.2.dev1}/inspy_logger/manifest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: inspy-logger
3
- Version: 3.0.dev5
3
+ Version: 3.0.2.dev1
4
4
  Summary: Colorable, scalable logger for CLI
5
5
  Home-page: https://github.com/Inspyre-Softworks/inSPy-Logger
6
6
  License: MIT
@@ -0,0 +1,105 @@
1
+ """
2
+
3
+ File:
4
+ inspy_logger/Scripts/main.py
5
+
6
+ Author:
7
+ Inspyre Softworks
8
+
9
+ """
10
+ INSPY_LOG_LEVEL = 'info'
11
+
12
+ from argparse import ArgumentParser
13
+ from inspy_logger.helpers.debug.environment import fetch_system_info
14
+ from inspy_logger.version import parse_version, get_full_version_name, PyPiVersionInfo
15
+ from rich import print
16
+ from rich.table import Table
17
+ import sys
18
+
19
+ # ---- SET UP PARSER --------------------------------
20
+
21
+ parser = ArgumentParser('inspy-logger-version', description='Displays version information for inspy-logger.')
22
+
23
+ parser.add_argument('-v', '--version', action='version', version=parse_version())
24
+
25
+ # ---- SET UP SUBPARSERS --------------------------------
26
+
27
+ subparsers = parser.add_subparsers(title='subcommands', dest='subcommand')
28
+
29
+ # ---- UPDATE COMMAND --------------------------------
30
+
31
+ update_parser =subparsers.add_parser('update', help='Checks for updates to inspy-logger.')
32
+
33
+ update_parser.add_argument('-p', '--pre-release', action='store_true', help='When checking for updates, include pre-release versions.')
34
+
35
+ # ---- DEBUG COMMAND --------------------------------
36
+
37
+ debug_parser = subparsers.add_parser('debug', help='Fetches a collection of environment information to help debug inspy-logger.')
38
+
39
+ debug_parser.add_argument('-C', '--copy-to-clipboard', action='store_true', help='Copies the debug information to the clipboard.')
40
+ debug_parser.add_argument('-P', '--print', action='store_true', help='Prints the debug information to the console.', default=True)
41
+
42
+ # ---- FORMAT FLAGS --------------------------------
43
+
44
+ fmt_grp = debug_parser.add_mutually_exclusive_group()
45
+
46
+ fmt_grp.add_argument('-M', '--markdown', action='store_true', help='Formats the debug information in Markdown.')
47
+
48
+ fmt_grp.add_argument('-J', '--json', action='store_true', help='Formats the debug information in JSON.')
49
+
50
+ fmt_grp.add_argument('-T', '--text', action='store_true', help='Formats the debug information in plain text.')
51
+
52
+ # Parse the arguments
53
+ parsed_args = parser.parse_args()
54
+
55
+ # END PARSER SETUP
56
+
57
+ if parsed_args.subcommand == 'update':
58
+ INCLUDE_PRE_RELEASE_FOR_UPDATE_CHECK = parsed_args.pre_release
59
+ else:
60
+ INCLUDE_PRE_RELEASE_FOR_UPDATE_CHECK = False
61
+
62
+
63
+
64
+
65
+
66
+ def main():
67
+
68
+ version = PyPiVersionInfo()
69
+
70
+ ACTIONS = {
71
+ 'default': version.print_version_info,
72
+ 'update': version.update,
73
+ 'debug': fetch_system_info,
74
+
75
+ }
76
+
77
+ if parsed_args.subcommand is None:
78
+ parsed_args.subcommand = 'default'
79
+
80
+ if parsed_args.subcommand == 'debug':
81
+
82
+ if (
83
+ parsed_args.markdown
84
+ or not parsed_args.json
85
+ and not parsed_args.text
86
+ ):
87
+ fmt = 'markdown'
88
+ elif parsed_args.json:
89
+ fmt = 'json'
90
+ else:
91
+ fmt = 'text'
92
+
93
+ fetch_system_info(copy_to_clipboard=parsed_args.copy_to_clipboard, format_type=fmt, print_to_console=parsed_args.print)
94
+
95
+
96
+
97
+ if parsed_args.subcommand in ACTIONS:
98
+ ACTIONS[parsed_args.subcommand]()
99
+
100
+
101
+
102
+ return parsed_args
103
+
104
+ if __name__ == '__main__':
105
+ main()
@@ -19,16 +19,3 @@ __authors__ = [
19
19
  ('Inspyre-Softworks', 'https://inspyre.tech'),
20
20
  ('Taylor-Jayde Blackstone', '<t.blackstone@inspyre.tech')
21
21
  ]
22
-
23
- '''
24
- File Change History:
25
- 11/5/22 - 4:21 AM (v2.1.2):
26
- - Added description to docstring. (target: 2.1.2)
27
-
28
- 11/5/22 - 4:41 AM:
29
- - Version bump (target: 2.1.3)
30
-
31
- 08/16/2023 - 13:49 (v3.0):
32
- - Version bump (v2.1.3 -> v3.0-dev.1)
33
-
34
- '''
@@ -251,10 +251,12 @@ class Logger:
251
251
  """
252
252
  self._log(logging.INFO, message, args=(), stacklevel=2)
253
253
 
254
+
254
255
  def warning(self, message):
255
256
  """
256
257
  Logs a warning message.
257
258
 
259
+
258
260
  Args:
259
261
  message (str): The message to log.
260
262
  """
@@ -264,6 +266,7 @@ class Logger:
264
266
  """
265
267
  Logs an error message.
266
268
 
269
+
267
270
  Args:
268
271
  message (str): The message to log.
269
272
  """
@@ -284,6 +287,7 @@ class Logger:
284
287
 
285
288
  return f'<Logger: {name} w/ level {self.logger.level} at {hex_id}{parent_part}>'
286
289
 
290
+
287
291
  @classmethod
288
292
  def create_logger_for_caller(cls):
289
293
  """
@@ -61,6 +61,16 @@ class Loggable:
61
61
  class_logger = None
62
62
 
63
63
  def __init__(self, parent_log_device=None, **kwargs):
64
+ """
65
+ Initializes an instance of the class.
66
+
67
+ Args:
68
+ parent_log_device (optional): The parent log device. Defaults to None.
69
+ **kwargs: Additional keyword arguments.
70
+
71
+ Returns:
72
+ None
73
+ """
64
74
  self.parent_log_device = parent_log_device
65
75
  self.__log_name = self.__class__.__name__
66
76
  if self.parent_log_device is not None:
@@ -0,0 +1,179 @@
1
+ import argparse
2
+ import json
3
+ import platform
4
+ import sys
5
+ import pyperclip
6
+ from pyperclip import copy
7
+
8
+ from inspy_logger.version import parse_version
9
+
10
+ VALID_DATA_FORMATS = [
11
+ 'markdown',
12
+ 'json',
13
+ 'text'
14
+ ]
15
+
16
+
17
+ def determine_execution_mode():
18
+ """
19
+ Determines the execution mode of the program.
20
+
21
+ Returns:
22
+ str:
23
+ Either 'Script' or 'REPL' depending on the execution mode.
24
+ """
25
+ return "REPL" if len(sys.argv) == 1 else "Script"
26
+
27
+
28
+ def get_operating_system_info():
29
+ """
30
+ Gets the operating system information.
31
+
32
+ Returns:
33
+ dict:
34
+ A dictionary containing the operating system information.
35
+ """
36
+
37
+ os_name = platform.system()
38
+
39
+ if os_name.lower() == 'windows':
40
+ from inspy_logger.helpers.debug.system.winx import get_win_version_info
41
+
42
+ return get_win_version_info()
43
+
44
+ return {
45
+ 'name': platform.system(),
46
+ 'version': platform.release()
47
+ }
48
+
49
+
50
+
51
+ def is_valid_data_format(data_format):
52
+ """
53
+ Checks if the specified data format is valid.
54
+
55
+ This function is case-insensitive and compares the specified data format against the `VALID_DATA_FORMATS` list.
56
+
57
+
58
+ Note:
59
+ Valid formats are as follows:
60
+ - markdown
61
+ - json
62
+ - text
63
+
64
+ Parameters:
65
+ data_format (str):
66
+ The format you'd like the data to be returned in.
67
+
68
+ Returns:
69
+ bool:
70
+ Whether the specified data format is valid.
71
+
72
+ """
73
+ return data_format.lower() in VALID_DATA_FORMATS
74
+
75
+
76
+ def get_system_info(format_type='markdown'):
77
+ """
78
+ Gathers system information and formats it based on the specified format type.
79
+
80
+ Parameters:
81
+ format_type (str):
82
+ The format type for the output. Options are 'markdown', 'json', 'text'.
83
+ Default is 'markdown'.
84
+
85
+ Note:
86
+ Valid formats are 'markdown', 'json', and 'text'.
87
+
88
+ Returns:
89
+ str:
90
+ A string containing the system information in the specified format.
91
+ """
92
+
93
+ if not is_valid_data_format(format_type):
94
+ raise ValueError(f"Invalid format type: {format_type}. Must be one of {VALID_DATA_FORMATS}")
95
+
96
+ # Gather system information
97
+ os_name = platform.system()
98
+ os_version = platform.release()
99
+
100
+ os_version_full = platform.version()
101
+
102
+ if os_name.lower() == 'windows' and os_version == '10':
103
+ from inspy_logger.helpers.debug.system.winx import is_win_11
104
+
105
+ if is_win_11(os_version_full):
106
+ os_version = '11'
107
+
108
+ os_version = f'{os_version} ({os_version_full})'
109
+
110
+
111
+
112
+ python_version = platform.python_version()
113
+
114
+
115
+
116
+ sys_info = {
117
+ 'os_name': platform.system(),
118
+ 'os_version': platform.release(),
119
+ 'python_version': platform.python_version(),
120
+ 'isl_version': parse_version(),
121
+ 'execution_mode': "REPL" if len(sys.argv) == 1 else "Script"
122
+ }
123
+
124
+ # Format the information
125
+ if format_type == 'markdown':
126
+ return f"""
127
+ 💻 **System Info:**
128
+ - 💾 **Operating System**:
129
+ - [x] {os_name} {os_version}
130
+ - 🐍 **Python Version**: {python_version}
131
+ - 🌈 **InSPy-Logger Version**: {sys_info['isl_version']}
132
+ - - [{sys_info['execution_mode'] == 'Script'}] Script
133
+ - [{sys_info['execution_mode'] == 'REPL'}] REPL
134
+ """.strip()
135
+ elif format_type == 'json':
136
+ return json.dumps({
137
+ "System Info": {
138
+ "Operating System": f"{os_name} {os_version}",
139
+ "Python Version": python_version,
140
+ "InSPy-Logger Version": sys_info['isl_version'],
141
+ "Execution Mode": sys_info['execution_mode']
142
+ }
143
+ }, indent=4)
144
+ else: # plain text
145
+ return f"""
146
+ System Info:
147
+ Operating System: {os_name} {os_version}
148
+ Python Version: {python_version}
149
+ InSPy-Logger Version: {sys_info['isl_version']}
150
+ Execution Mode: {sys_info['execution_mode']}
151
+ """.strip()
152
+
153
+
154
+ def fetch_system_info(copy_to_clipboard=False, format_type='markdown', print_to_console=None):
155
+ """
156
+ Fetches system information and formats it based on the specified format type.
157
+
158
+ Parameters:
159
+ copy_to_clipboard (bool):
160
+ Whether to copy the output to the clipboard. Default is False.
161
+
162
+ format_type (str):
163
+ The format type for the output. Options are 'markdown',
164
+ 'json', 'text'. Default is 'markdown'.
165
+
166
+ print_to_console (bool):
167
+ Whether to print the output to the console. Default is None.
168
+
169
+ Note:
170
+ If this is None, it will default to True.
171
+
172
+ Returns:
173
+ str:
174
+ A string containing the system information in the specified format.
175
+ """
176
+ info = get_system_info(format_type)
177
+ if copy_to_clipboard:
178
+ copy(info)
179
+ return info
@@ -0,0 +1,64 @@
1
+ import platform
2
+ import multiprocessing
3
+ import psutil
4
+
5
+
6
+ # Function to check if the system architecture is 64-bit
7
+ def is_64bit():
8
+ """
9
+ Checks if the system architecture is 64-bit.
10
+
11
+ Returns:
12
+ bool: True if the system architecture is 64-bit, False otherwise.
13
+ """
14
+ return platform.architecture()[0] == '64bit'
15
+
16
+
17
+ # Function to get the number of cores
18
+ def number_of_cores():
19
+ """
20
+ Gets the number of cores in the system.
21
+
22
+ Returns:
23
+ int: The number of cores in the system.
24
+ """
25
+ return multiprocessing.cpu_count()
26
+
27
+
28
+ # Function to get the processor core information
29
+ def get_processor_core_info():
30
+ """
31
+ Gets the processor core information.
32
+
33
+ Returns:
34
+ dict: A dictionary containing the total number of cores, the number of physical cores, and the number of logical cores.
35
+ """
36
+ phys = psutil.cpu_count(logical=False)
37
+ total = number_of_cores()
38
+
39
+ return {
40
+ 'total': total,
41
+ 'physical': phys,
42
+ 'logical': total - phys
43
+ }
44
+
45
+
46
+ # Function to get the processor information
47
+ def _get_processor_info():
48
+ """
49
+ Gets the processor information.
50
+
51
+ Returns:
52
+ dict: A dictionary containing the name of the processor, the architecture of the processor, and the core information of the processor.
53
+ """
54
+ cores = get_processor_core_info()
55
+
56
+ return {
57
+ 'name': platform.processor(),
58
+ 'architecture': '64-bit' if is_64bit() else '32-bit',
59
+ 'cores': get_processor_core_info()
60
+ }
61
+
62
+
63
+ # A dictionary containing the processor information
64
+ PROCESSOR_INFO = _get_processor_info()
@@ -0,0 +1,25 @@
1
+ from packaging import version
2
+ import platform
3
+
4
+ WIN_11_VERSION = version.parse('10.0.2200')
5
+
6
+
7
+ def is_win_11(os_version):
8
+ return version.parse(os_version) >= WIN_11_VERSION
9
+
10
+ def is_64bit():
11
+ return platform.architecture()[0] == '64bit'
12
+
13
+ def get_win_version():
14
+ release, build_num, service_pack, _ = platform.win32_ver()
15
+
16
+ if release == '10' and is_win_11(build_num):
17
+ release = '11'
18
+
19
+ ver = {
20
+ 'release': f'Windows {release}',
21
+ 'build': build_num,
22
+ 'service_pack': service_pack.replace('SP', ''),
23
+
24
+
25
+ }
@@ -27,8 +27,7 @@ from __future__ import annotations
27
27
  from urllib.parse import quote as url_safe
28
28
 
29
29
  import requests
30
-
31
- from inspy_logger.helpers.cache import TLDCache
30
+ from public_suffix_list import PublicSuffixList
32
31
 
33
32
  DEFAULT_TEST_HOSTS = [
34
33
  ['https', 'inspyre', 'tech'],
@@ -44,10 +43,11 @@ hosts = []
44
43
  :obj:`list`[:obj:`Host`]:
45
44
  A list of host objects that will be used to connect to.
46
45
  """
46
+ psl = PublicSuffixList()
47
47
 
48
- TLD_CACHE = TLDCache()
48
+ URL_SUFFIXES = psl._suffixes
49
49
 
50
- VALID_TLDS = TLD_CACHE.TLDs
50
+ VALID_TLDS = URL_SUFFIXES
51
51
 
52
52
 
53
53
  class Host():
@@ -169,7 +169,7 @@ class Host():
169
169
  def tld(self, new):
170
170
  if not isinstance(new, str):
171
171
  raise TypeError(f"New value must be a string not {type(new)}")
172
- if new not in TLD_CACHE.TLDs:
172
+ if new not in URL_SUFFIXES:
173
173
  raise ValueError("TLD must be valid!")
174
174
 
175
175
  self.__tld = new
@@ -0,0 +1,57 @@
1
+ class ByteConverter:
2
+ """
3
+ A class to convert between various units of digital storage.
4
+
5
+ Supported units: Bytes, Kilobytes, Megabytes, Gigabytes, Terabytes, Petabytes, Exabytes, Zettabytes, Yottabytes,
6
+ Kibibytes, Mebibytes, Gibibytes, Tebibytes, Pebibytes, Exbibytes, Zebibytes, Yobibytes.
7
+
8
+ Methods:
9
+ convert(value, from_unit, to_unit): Converts the given value from one unit to another.
10
+ """
11
+
12
+ # Dictionary to store conversion rates relative to bytes
13
+ _conversion_rates = {
14
+ 'bytes': 1,
15
+ 'kilobytes': 1000,
16
+ 'megabytes': 1000 ** 2,
17
+ 'gigabytes': 1000 ** 3,
18
+ 'terabytes': 1000 ** 4,
19
+ 'petabytes': 1000 ** 5,
20
+ 'exabytes': 1000 ** 6,
21
+ 'zettabytes': 1000 ** 7,
22
+ 'yottabytes': 1000 ** 8,
23
+ 'kibibytes': 1024,
24
+ 'mebibytes': 1024 ** 2,
25
+ 'gibibytes': 1024 ** 3,
26
+ 'tebibytes': 1024 ** 4,
27
+ 'pebibytes': 1024 ** 5,
28
+ 'exbibytes': 1024 ** 6,
29
+ 'zebibytes': 1024 ** 7,
30
+ 'yobibytes': 1024 ** 8
31
+ }
32
+
33
+ @classmethod
34
+ def convert(cls, value: float, from_unit: str, to_unit: str) -> float:
35
+ """
36
+ Converts the given value from one unit to another.
37
+
38
+ Args:
39
+ value (float): The value to convert.
40
+ from_unit (str): The unit to convert from. Must be one of the supported units.
41
+ to_unit (str): The unit to convert to. Must be one of the supported units.
42
+
43
+ Returns:
44
+ float: The converted value in the target unit.
45
+
46
+ Raises:
47
+ ValueError: If an unsupported unit is specified.
48
+ """
49
+ from_unit = from_unit.lower()
50
+ to_unit = to_unit.lower()
51
+
52
+ if from_unit not in cls._conversion_rates or to_unit not in cls._conversion_rates:
53
+ raise ValueError("Unsupported unit. Supported units are: " + ', '.join(cls._conversion_rates.keys()))
54
+
55
+ # Convert the value to bytes first, then to the target unit
56
+ value_in_bytes = value * cls._conversion_rates[from_unit]
57
+ return value_in_bytes / cls._conversion_rates[to_unit]
@@ -0,0 +1,240 @@
1
+ import requests
2
+ from packaging import version as pkg_version
3
+ import sys
4
+ from rich.table import Table
5
+ from rich import print
6
+
7
+ # Constants
8
+ RELEASE_MAP = {
9
+ 'dev': 'Development Build',
10
+ 'alpha': 'Alpha Build',
11
+ 'beta': 'Beta Build',
12
+ 'rc': 'Release Candidate Build',
13
+ 'final': 'Final Release Build'
14
+ }
15
+
16
+ __VERSION__ = {
17
+ 'major': 3,
18
+ 'minor': 0,
19
+ 'patch': 2,
20
+ 'release': 'dev',
21
+ 'release_num': 1
22
+ }
23
+
24
+
25
+ def parse_version():
26
+ """
27
+ Parses the version information into a string.
28
+
29
+ Returns:
30
+ str: The version information.
31
+
32
+ Since:
33
+ v1.0
34
+ """
35
+ version = f'{__VERSION__["major"]}.{__VERSION__["minor"]}.{__VERSION__["patch"]}'
36
+
37
+ if __VERSION__['release'] != 'final':
38
+ version += f'-{__VERSION__["release"]}.{__VERSION__["release_num"]}'
39
+
40
+ return version
41
+
42
+
43
+ def get_full_version_name():
44
+ """
45
+ Gets the full version name.
46
+
47
+ Returns:
48
+ str: The full version name.
49
+
50
+ Since:
51
+ v1.0
52
+ """
53
+ ver = parse_version()
54
+ ver = ver.split('-')[0]
55
+
56
+ release_type = RELEASE_MAP[__VERSION__["release"]]
57
+ release_num = __VERSION__["release_num"]
58
+ release_str = f" {release_type} {'' if __VERSION__['release'].lower() == 'final' else f'({release_num})'}"
59
+ return f'v{ver}{release_str}'
60
+
61
+
62
+ class PyPiVersionInfo:
63
+ """
64
+ A class to represent the version information for this package from PyPi.
65
+
66
+ Attributes:
67
+ latest_stable (packaging.version.Version):
68
+ The latest stable version of the package on PyPi.
69
+
70
+ latest_pre_release (packaging.version.Version):
71
+ The latest pre-release version of the package.
72
+ Since:
73
+ v3.0
74
+ """
75
+ __url = 'https://pypi.org/pypi/inspy-logger/json'
76
+ __installed = parse_version()
77
+ __checked_for_update = False
78
+ __newer_available_version = None
79
+
80
+ def __init__(self, include_pre_release_for_update_check=False):
81
+ self.__latest_stable = None
82
+ self.__latest_pre_release = None
83
+ self.__all_versions = None
84
+ self.include_pre_release_for_update_check = include_pre_release_for_update_check
85
+ self.__query_versions()
86
+
87
+ @property
88
+ def all_versions(self):
89
+ if self.__all_versions is None:
90
+ self.__query_versions()
91
+ return sorted([pkg_version.parse(v) for v in self.__all_versions])
92
+
93
+ @property
94
+ def checked_for_update(self):
95
+ return self.__checked_for_update
96
+
97
+ @property
98
+ def installed(self):
99
+ """
100
+ Gets the installed version of the package.
101
+ """
102
+ return pkg_version.parse(self.__installed)
103
+
104
+ @property
105
+ def installed_newer_than_latest(self):
106
+ return self.installed > self.latest
107
+
108
+ @property
109
+ def latest(self):
110
+ return (
111
+ self.all_versions[-1]
112
+ if self.include_pre_release_for_update_check
113
+ else self.latest_stable
114
+ )
115
+
116
+ @property
117
+ def latest_stable(self):
118
+ """
119
+ Gets the latest stable version of the package on PyPi.
120
+ """
121
+ if self.__latest_stable is None:
122
+ self.__query_versions()
123
+ return pkg_version.parse(self.__latest_stable)
124
+
125
+ @property
126
+ def latest_pre_release(self):
127
+ """
128
+ Gets the latest pre-release version of the package.
129
+ """
130
+ if self.__all_versions is None:
131
+ self.__query_versions()
132
+ pre_release_versions = [v for v in self.all_versions if v.is_prerelease]
133
+ return pre_release_versions[-1] if pre_release_versions else None
134
+
135
+ @property
136
+ def newer_available_version(self):
137
+
138
+ if self.__newer_available_version is None:
139
+ self.check_for_update()
140
+
141
+ return self.__newer_available_version
142
+
143
+ def __query_versions(self):
144
+ """
145
+ Queries the versions from PyPi.
146
+ """
147
+ try:
148
+ response = requests.get(self.__url)
149
+ response.raise_for_status()
150
+ data = response.json()
151
+
152
+ self.__all_versions = list(data['releases'].keys())
153
+ self.__latest_stable = data['info']['version']
154
+ except requests.RequestException as e:
155
+ # Handle connection errors, HTTP errors, etc.
156
+ print(f"Error querying PyPi: {e}")
157
+
158
+ @property
159
+ def update_available(self):
160
+ """
161
+ Checks if an update is available.
162
+ """
163
+ if self.__newer_available_version is None:
164
+ self.check_for_update()
165
+ return self.__newer_available_version is not None
166
+
167
+ def check_for_update(self, include_pre_releases=False):
168
+ latest_version = self.latest_stable
169
+
170
+ if include_pre_releases or self.include_pre_release_for_update_check:
171
+ latest_version = max(latest_version, self.latest_pre_release)
172
+
173
+ if latest_version > self.installed:
174
+ self.__newer_available_version = latest_version
175
+
176
+ self.__checked_for_update = True
177
+
178
+ return latest_version > self.installed
179
+
180
+ def update(self):
181
+ """
182
+ Checks for updates to inspy-logger.
183
+
184
+ Returns:
185
+ None`
186
+
187
+ Since:
188
+ v3.0
189
+ """
190
+
191
+ local_newer_statement = 'Local version is newer than latest version. This is likely a development build.'
192
+
193
+ if not self.installed_newer_than_latest:
194
+ local_newer_statement = ''
195
+
196
+ try:
197
+ if self.update_available:
198
+ print(f'\n\n[bold green]Update Available![/bold green] New version: '
199
+ f'[bold cyan]{self.update_available}[/bold cyan]')
200
+ else:
201
+ print(f'\n\n[bold green]No update available.[/bold green] Current version: '
202
+ f'[bold cyan]{parse_version()}[/bold cyan] {local_newer_statement}')
203
+
204
+ except Exception as e:
205
+ print(f'An error occurred during the update check: {str(e)}')
206
+
207
+ def print_version_info(self):
208
+ """
209
+ Print version information in a formatted table.
210
+
211
+ This function creates a table using the `Table` class and populates it with version information about the current Python environment. It then prints the table to the console.
212
+
213
+ Parameters:
214
+ self: The current instance of the class.
215
+
216
+ Returns:
217
+ None
218
+ """
219
+
220
+ # Create a table
221
+
222
+ table = Table(show_header=False, show_lines=True, expand=True, border_style='bright_blue',
223
+ row_styles=['none', 'dim'])
224
+
225
+ # Add columns
226
+ table.add_column('Property', style='cyan', width=20)
227
+ table.add_column('Value', justify='center')
228
+
229
+ # Add rows
230
+ table.add_row('Version', parse_version(), )
231
+ table.add_row('Full Version Name', get_full_version_name())
232
+
233
+ if self.update_available:
234
+ table.add_row('Update Available', '[bold green]Yes[/bold green]')
235
+ table.add_row('Latest Version', f'{self.new_version_available_num}')
236
+
237
+ table.add_row('Python Executable Path', sys.executable)
238
+ table.add_row('Python Version', sys.version)
239
+
240
+ print(table)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "inspy-logger"
3
- version = "3.0-dev.5"
3
+ version = "3.0.2-dev.1"
4
4
  description = "Colorable, scalable logger for CLI"
5
5
  authors = ["Taylor-Jayde Blackstone <tayjaybabee@gmail.com>"]
6
6
  homepage = 'https://github.com/Inspyre-Softworks/inSPy-Logger'
@@ -62,6 +62,10 @@ sphinxcontrib-autoprogram = "^0.1.8"
62
62
  [tool.poetry.group.dev.dependencies]
63
63
  poetry-plugin-export = "^1.6.0"
64
64
 
65
+
66
+ [tool.poetry.group.debug.dependencies]
67
+ pyperclip = "^1.8.2"
68
+
65
69
  [build-system]
66
70
  requires = ["poetry-core>=1.0.0"]
67
71
  build-backend = "poetry.core.masonry.api"
@@ -1,91 +0,0 @@
1
- """
2
-
3
- File:
4
- inspy_logger/Scripts/main.py
5
-
6
- Author:
7
- Inspyre Softworks
8
-
9
- """
10
- INSPY_LOG_LEVEL = 'info'
11
-
12
- from inspy_logger.version import parse_version, get_full_version_name, PyPiVersionInfo
13
- import sys
14
- from rich import print
15
- from rich.table import Table
16
- from argparse import ArgumentParser
17
-
18
-
19
- parser = ArgumentParser('inspy-logger-version', description='Displays version information for inspy-logger.')
20
-
21
- parser.add_argument('-v', '--version', action='version', version=parse_version())
22
-
23
- subparsers = parser.add_subparsers(title='subcommands', dest='subcommand')
24
-
25
- update_parser =subparsers.add_parser('update', help='Checks for updates to inspy-logger.')
26
-
27
- update_parser.add_argument('-p', '--pre-release', action='store_true', help='When checking for updates, include pre-release versions.')
28
-
29
- parsed_args = parser.parse_args()
30
-
31
-
32
- if parsed_args.subcommand == 'update':
33
- INCLUDE_PRE_RELEASE_FOR_UPDATE_CHECK = parsed_args.pre_release
34
- else:
35
- INCLUDE_PRE_RELEASE_FOR_UPDATE_CHECK = False
36
-
37
-
38
- pypi_info = PyPiVersionInfo(include_pre_release_for_update_check=INCLUDE_PRE_RELEASE_FOR_UPDATE_CHECK)
39
-
40
-
41
- def update():
42
- """
43
- Checks for updates to inspy-logger.
44
-
45
- Returns:
46
- None
47
-
48
- Since:
49
- v3.0
50
- """
51
- try:
52
- if pypi_info.update_available:
53
- print(f'\n\n[bold green]Update Available![/bold green] New version: [bold cyan]{pypi_info.new_version_available}[/bold cyan]')
54
- else:
55
- print(f'\n\n[bold green]No update available.[/bold green] Current version: [bold cyan]{parse_version()}[/bold cyan] {"which is newer than the available version on PyPi.org" if pypi_info.installed_newer_than_latest else ""}')
56
- except Exception as e:
57
- print(f'An error occurred during the update check: {str(e)}')
58
-
59
-
60
- def get_version_info():
61
- # Create a table
62
- table = Table(show_header=False, show_lines=True, expand=True, border_style='bright_blue',
63
- row_styles=['none', 'dim'])
64
-
65
- # Add columns
66
- table.add_column('Property', style='cyan', width=20)
67
- table.add_column('Value', justify='center')
68
-
69
- # Add rows
70
- table.add_row('Version', parse_version(), )
71
- table.add_row('Full Version Name', get_full_version_name())
72
-
73
- if pypi_info.update_available:
74
- table.add_row('Update Available', '[bold green]Yes[/bold green]')
75
- table.add_row('Latest Version', f'{pypi_info.new_version_available_num}')
76
-
77
- table.add_row('Python Executable Path', sys.executable)
78
- table.add_row('Python Version', sys.version)
79
-
80
- print(table)
81
-
82
-
83
-
84
- def main():
85
-
86
- return update() if parsed_args.subcommand == 'update' else get_version_info()
87
-
88
-
89
-
90
- if __name__ == '__main__':
91
- main()
@@ -1,140 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from os import makedirs
4
- from pathlib import Path
5
- from pickle import dump as pkl_dump
6
- from pickle import load as pkl_load
7
- from time import time
8
- from appdirs import user_cache_dir
9
- from domain_suffixes.suffixes import Suffixes
10
- from humanize.time import precisedelta
11
-
12
- PICKLE_FILEPATH = Path(
13
- user_cache_dir(
14
- appname='InspyLogger',
15
- appauthor='Inspyre-Softworks'
16
- )
17
- ).joinpath('tld.pkl')
18
-
19
-
20
- class TLDCache(object):
21
-
22
- parent_dir = PICKLE_FILEPATH.parent
23
-
24
- def __init__(self):
25
- self.__created_ts = None
26
- self.__pickle_fp_exists = None
27
- self.__num_tlds = None
28
- self.__tlds = None
29
- self.__data = None
30
- self.__last_renewal_ts = None
31
- self.__renewal_hist = None
32
-
33
- def build(self):
34
- self.__data = {
35
- 'created_ts': time(),
36
- 'renewal_data': {
37
- 'last_ts': None,
38
- 'history': [],
39
- },
40
- 'tlds': self.fetch_from_source()
41
- }
42
-
43
- return self.__data
44
-
45
- @staticmethod
46
- def load(self):
47
- """ Load the pickle file for the cache.
48
-
49
- Will attempt to load the pickle file into memory from the path indicated by :obj:`PICKLE_FILEPATH`
50
-
51
- Raises:
52
- :exception:`FileNotFoundError`: If the pickle file does not exist.
53
-
54
- """
55
- with open(PICKLE_FILEPATH, 'rb') as pfp:
56
- res = pkl_load(pfp)
57
-
58
- return res
59
-
60
- def write(self):
61
- """ Write the pickle file.
62
-
63
- Write the contents of the cache to the file-system path indicated by :obj:`PICKLE_FILEPATH`
64
-
65
- Raises:
66
- :exception:`PermissionError`: If the pickle file can't be written due to permissions writing the file to storage.
67
- """
68
-
69
- if not self.parent_dir.exists():
70
- makedirs(self.parent_dir, exist_ok=True)
71
-
72
- with open(PICKLE_FILEPATH, 'wb') as pfp:
73
- pkl_dump(self.__data, pfp)
74
-
75
- @staticmethod
76
- def fetch_from_source(self):
77
- """ Fetch the top-level-domain list from the source
78
-
79
- Returns:
80
- :class:`list`: A list of all known top-level domain suffixes from the domain-suffixes package.
81
- """
82
- suffixes = Suffixes()
83
- return suffixes.get_all_tlds()
84
-
85
- @property
86
- def contents(self) -> dict:
87
- if self.__data is None:
88
- try:
89
- self.__data = self.load()
90
- except FileNotFoundError:
91
- self.__data = self.build()
92
- self.write()
93
-
94
- return self.__data
95
-
96
- @property
97
- def TLDs(self) -> list:
98
- """ Returns a list of TLDs (top-level domains) for validating against """
99
- return self.contents['tlds']
100
-
101
- @property
102
- def created_timestamp(self) -> float:
103
- """
104
- An alias for :attr:`created_ts`
105
-
106
- Returns:
107
- created_ts (float):
108
- The time (in seconds since Epoch) when the cache was created.
109
-
110
- """
111
- return self.created_ts
112
-
113
- @property
114
- def created_ts(self) -> float:
115
- """
116
- The time (in seconds since Epoch) when the cache was created
117
- """
118
- return self.contents['created_ts']
119
-
120
- @property
121
- def age_in_seconds(self) -> float:
122
- """
123
- The number of seconds that have elapsed since creation of the cache.
124
- """
125
- return time() - self.created_ts
126
-
127
- @property
128
- def age(self) -> str:
129
- return precisedelta(self.age_in_seconds)
130
-
131
-
132
- """
133
- File Change History:
134
-
135
- 11/5/22 - 4:24 AM (target: v2.1.2):
136
- - Code cleanup
137
- - Transform from class functions to static methods;
138
- - `TLDCache.load`
139
- - `TLDCache.fetch_from_source`
140
- """
@@ -1,287 +0,0 @@
1
- import requests
2
- import packaging
3
- from packaging import version as pkg_version
4
-
5
- RELEASE_MAP = {
6
- 'dev': 'Development Build',
7
- 'alpha': 'Alpha Build',
8
- 'beta': 'Beta Build',
9
- 'rc': 'Release Candidate Build',
10
- 'final': 'Final Release Build'
11
- }
12
-
13
-
14
- __VERSION__ = {
15
- 'major': 3,
16
- 'minor': 0,
17
- 'patch': 0,
18
- 'release': 'dev',
19
- 'release_num': 5
20
- }
21
-
22
-
23
- def parse_version():
24
- """
25
- Parses the version information into a string.
26
-
27
- Returns:
28
- str: The version information.
29
-
30
- Since:
31
- v1.0
32
- """
33
- version = f'{__VERSION__["major"]}.{__VERSION__["minor"]}.{__VERSION__["patch"]}'
34
-
35
- if __VERSION__['release'] != 'final':
36
- version += f'-{__VERSION__["release"]}.{__VERSION__["release_num"]}'
37
-
38
- return version
39
-
40
-
41
- def get_full_version_name():
42
- """
43
- Gets the full version name.
44
-
45
- Returns:
46
- str: The full version name.
47
-
48
- Since:
49
- v1.0
50
- """
51
- ver = parse_version()
52
- ver = ver.split('-')[0]
53
- return f'v{ver} {RELEASE_MAP[__VERSION__["release"]]} ({__VERSION__["release_num"]})'
54
-
55
-
56
- def get_pypi_info():
57
- """
58
- Gets the latest version information from PyPI.
59
-
60
- Returns:
61
- dict: The latest version information.
62
-
63
- Since:
64
- v1.0
65
- """
66
- url = 'https://pypi.org/pypi/inspy-logger/json'
67
- resp = requests.get(url)
68
- resp.raise_for_status()
69
- resp = resp.json()
70
- return resp
71
-
72
-
73
- class PyPiVersionInfo:
74
- """
75
- A class to represent the version information for this package from PyPi.
76
-
77
- Attributes:
78
- latest_stable (str):
79
- The latest stable version of the package on PyPi.
80
-
81
- latest_pre_release (str):
82
- The latest pre-release version of the package.
83
- Since:
84
- v3.0
85
- """
86
- __url = 'https://pypi.org/pypi/inspy-logger/json'
87
- __queried = False
88
- __installed = parse_version()
89
- __include_pre_release_for_update_check = False
90
- __new_version_available_num = None
91
- __checked_for_update = False
92
- __installed_newer_than_latest = None
93
-
94
- def __init__(self, include_pre_release_for_update_check=__include_pre_release_for_update_check):
95
- self.__latest_stable = None
96
- self.__latest_pre_release = None
97
-
98
- self.__all_versions = None
99
-
100
- self.include_pre_release_for_update_check = include_pre_release_for_update_check
101
-
102
- self.__query_versions()
103
-
104
- @property
105
- def all_versions(self):
106
- return self.__all_versions
107
-
108
- @property
109
- def checked_for_update(self):
110
- return self.__checked_for_update
111
-
112
- @property
113
- def include_pre_release_for_update_check(self):
114
- """
115
- Gets whether pre-release versions should be included when checking for updates.
116
-
117
- Returns:
118
- bool:
119
- True if pre-release versions should be included, False otherwise.
120
-
121
- Since:
122
- v3.0
123
-
124
- """
125
- return self.__include_pre_release_for_update_check
126
-
127
- @property
128
- def installed_newer_than_latest(self):
129
- return self.installed > self.latest_stable
130
-
131
- @include_pre_release_for_update_check.setter
132
- def include_pre_release_for_update_check(self, new):
133
- if isinstance(new, bool):
134
- self.__include_pre_release_for_update_check = new
135
- else:
136
- raise TypeError(f'Expected bool, received {type(new)}.')
137
-
138
- @property
139
- def installed(self):
140
- """
141
- Gets the installed version of the package.
142
-
143
- Returns:
144
- packaging.version.Version:
145
- The installed version of the package.
146
-
147
- """
148
- return pkg_version.parse(self.__installed)
149
-
150
- @property
151
- def latest_stable(self):
152
- """
153
- Gets the latest stable version of the package on PyPi.
154
-
155
- Returns:
156
- str: The latest stable version of the package on PyPi.
157
-
158
- Since:
159
- v3.0
160
- """
161
- if self.__latest_stable is not None:
162
- return pkg_version.parse(self.__latest_stable)
163
-
164
- return self.__latest_stable
165
-
166
- @property
167
- def latest_pre_release(self):
168
- """
169
- Gets the latest pre-release version of the package.
170
-
171
- Returns:
172
- str: The latest pre-release version of the package.
173
-
174
- Since:
175
- v3.0
176
- """
177
- if self.all_versions is not None and self.__latest_pre_release is None:
178
- most_recent = pkg_version.parse(self.all_versions[-1])
179
- if most_recent.is_prerelease:
180
- self.__latest_pre_release = most_recent
181
-
182
- return self.__latest_pre_release
183
-
184
- @property
185
- def new_version_available_num(self):
186
- """
187
- Gets the number of the latest version available on PyPi.
188
-
189
- Returns:
190
- int: The number of the latest version available on PyPi.
191
-
192
- Since:
193
- v3.0
194
- """
195
- if not self.checked_for_update:
196
- self.update_available
197
- return self.__new_version_available_num
198
-
199
- @property
200
- def queried(self):
201
- """
202
- Checks if the versions have been queried.
203
-
204
- Returns:
205
- bool: True if the versions have been queried, False otherwise.
206
-
207
- Since:
208
- v3.0
209
- """
210
- return self.__queried
211
-
212
- @property
213
- def update_available(self):
214
- """
215
- Checks if an update is available.
216
-
217
- Returns:
218
- bool: True if an update is available, False otherwise.
219
-
220
- Since:
221
- v3.0
222
- """
223
- if not self.queried:
224
- #print('Querying versions')
225
- self.__query_versions()
226
-
227
- if not self.checked_for_update:
228
- #print('Checking for update')
229
- self.__compare_latest()
230
- self.__checked_for_update = True
231
-
232
- if self.new_version_available_num is None and not self.queried:
233
- #print('Getting latest')
234
- self.__compare_latest()
235
-
236
- return self.new_version_available_num is not None
237
-
238
-
239
-
240
-
241
- def __query_versions(self):
242
- """
243
- Queries the versions from PyPi.
244
-
245
- Returns:
246
- dict: The versions from PyPi.
247
-
248
- Since:
249
- v3.0
250
- """
251
- if not self.queried:
252
- resp = requests.get(self.__url)
253
- resp.raise_for_status()
254
- #print(resp.status_code)
255
- resp = resp.json()
256
-
257
- self.__all_versions = list(resp['releases'].keys())
258
- #print(self.all_versions)
259
-
260
- self.__latest_stable = resp['info']['version']
261
- #print(self.latest_stable)
262
-
263
-
264
-
265
- self.__queried = True
266
-
267
- def __compare_latest(self):
268
- latest = self.__get_latest()
269
-
270
- if latest is not None and latest > self.installed:
271
- self.__new_version_available_num = latest
272
-
273
- def __get_latest(self):
274
- if not self.queried:
275
- #print('Querying versions')
276
- self.__query_versions()
277
-
278
- if self.include_pre_release_for_update_check:
279
- latest = self.latest_pre_release
280
-
281
- else:
282
- latest = self.latest_stable
283
-
284
- #print(f'Using latest {latest}')
285
-
286
-
287
- return latest