python-backpack 1.1.4__tar.gz → 2.0.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.
@@ -1,12 +1,12 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: python-backpack
3
- Version: 1.1.4
3
+ Version: 2.0.0
4
4
  Summary: A collection of personal scripts for json, File/Folder Operations, String Validation, Custom Errors, Cache and stuff.
5
+ License-File: LICENSE
5
6
  Author: Maximiliano Rocamora
6
- Requires-Python: >=3.9,<4.0
7
+ Requires-Python: >=3.11,<4.0
7
8
  Classifier: Programming Language :: Python :: 3
8
- Classifier: Programming Language :: Python :: 3.9
9
- Classifier: Programming Language :: Python :: 3.10
10
9
  Classifier: Programming Language :: Python :: 3.11
11
10
  Classifier: Programming Language :: Python :: 3.12
12
11
  Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
@@ -5,13 +5,16 @@
5
5
  # ----------------------------------------------------------------------------------------
6
6
  from datetime import datetime, timedelta, timezone
7
7
  from functools import lru_cache, wraps
8
+ from typing import Any, Callable, TypeVar, cast
8
9
 
9
10
  from backpack.logger import get_logger
10
11
 
11
12
  log = get_logger('Python Backpack - Cache')
12
13
 
14
+ F = TypeVar('F', bound=Callable[..., Any])
13
15
 
14
- def timed_lru_cache(seconds: int, maxsize: int = 128):
16
+
17
+ def timed_lru_cache(seconds: int, maxsize: int = 128) -> Callable[[F], F]:
15
18
  """Lru_cache with expiration time.
16
19
 
17
20
  Args:
@@ -36,10 +39,10 @@ def timed_lru_cache(seconds: int, maxsize: int = 128):
36
39
 
37
40
  """
38
41
 
39
- def wrapper_cache(func):
40
- func = lru_cache(maxsize=maxsize)(func)
41
- func.lifetime = timedelta(seconds=seconds)
42
- func.expiration = datetime.now(timezone.utc) + func.lifetime
42
+ def wrapper_cache(func: F) -> F:
43
+ cached_func = cast(Any, lru_cache(maxsize=maxsize)(func))
44
+ cached_func.lifetime = timedelta(seconds=seconds)
45
+ cached_func.expiration = datetime.now(timezone.utc) + cached_func.lifetime
43
46
 
44
47
  @wraps(func)
45
48
  def wrapped_func(*args, force_clear: bool = False, show_log: bool = False, **kwargs):
@@ -54,15 +57,15 @@ def timed_lru_cache(seconds: int, maxsize: int = 128):
54
57
  function result
55
58
  """
56
59
 
57
- if force_clear or datetime.now(timezone.utc) >= func.expiration:
60
+ if force_clear or datetime.now(timezone.utc) >= cached_func.expiration:
58
61
  if show_log:
59
- log.debug(f'Cache cleared for {func.__name__}')
62
+ log.debug(f'Cache cleared for {cached_func.__name__}')
60
63
 
61
- func.cache_clear()
62
- func.expiration = datetime.now(timezone.utc) + func.lifetime
64
+ cached_func.cache_clear()
65
+ cached_func.expiration = datetime.now(timezone.utc) + cached_func.lifetime
63
66
 
64
- return func(*args, **kwargs)
67
+ return cached_func(*args, **kwargs)
65
68
 
66
- return wrapped_func
69
+ return cast(F, wrapped_func)
67
70
 
68
71
  return wrapper_cache
@@ -9,6 +9,7 @@ us.save(someDict)
9
9
  data = us.load()
10
10
 
11
11
  """
12
+
12
13
  # ----------------------------------------------------------------------------------------
13
14
  import os
14
15
 
@@ -59,14 +60,16 @@ class JsonUserSettings:
59
60
 
60
61
  return True
61
62
 
62
- def save_settings(self, data=False) -> bool:
63
+ def save_settings(self, data: dict | None = None) -> bool | None:
63
64
  """Saves a dictionary into a json file (os user path).
64
65
 
65
66
  Args:
66
- data (dictionary) : info dictionary to save, if set to False,
67
+ data (dictionary): info dictionary to save, if not provided,
67
68
  saves instead local self.user_data property
69
+ Returns:
70
+ bool | None: True if file was saved, False if error, None if no data to save.
68
71
  """
69
- if not data:
72
+ if data is None:
70
73
  data = self.user_data
71
74
 
72
75
  r = json_save(data, self.filepath)
@@ -74,7 +77,7 @@ class JsonUserSettings:
74
77
  log.info('json settings file saved! [%s]', self.filepath)
75
78
  return r
76
79
 
77
- def load_settings(self) -> dict:
80
+ def load_settings(self) -> dict | bool:
78
81
  """Load json file from path and returns its contents."""
79
82
  try:
80
83
  return json_load(self.filepath)
@@ -8,8 +8,11 @@ import contextlib
8
8
  import re
9
9
 
10
10
 
11
- def reformat_input_string(
12
- input_string: str, under_spaces: bool = True, under_hyphen: bool = True
11
+ def normalize_input_string(
12
+ input_string: str,
13
+ under_spaces: bool = True,
14
+ under_hyphen: bool = True,
15
+ replacer: str = '_',
13
16
  ) -> str:
14
17
  """Reformat the user input string.
15
18
 
@@ -20,6 +23,7 @@ def reformat_input_string(
20
23
  input_string (str): input string to reformat
21
24
  under_spaces (bool, optional): replaces spaces. Defaults to True.
22
25
  under_hyphen (bool, optional): replaces hyphens. Defaults to True.
26
+ replacer (str, optional): char to replace spaces and hyphens. Defaults to '_'.
23
27
 
24
28
  Returns:
25
29
  str: reformatted string
@@ -30,13 +34,13 @@ def reformat_input_string(
30
34
  regex = re.sub('[^A-Za-z0-9 _-]+', '', input_string)
31
35
 
32
36
  for char in regex:
33
- # replacing " " for "_"
37
+ # replacing " " for replacer
34
38
  if under_spaces and char == ' ':
35
- regex = regex.replace(' ', '_')
39
+ regex = regex.replace(' ', replacer)
36
40
 
37
- # replacing "-" for "_"
41
+ # replacing "-" for replacer
38
42
  if under_hyphen and char == '-':
39
- regex = regex.replace('-', '_')
43
+ regex = regex.replace('-', replacer)
40
44
 
41
45
  return regex
42
46
 
@@ -85,6 +89,7 @@ def camelcase_to_snakecase(input_string: str) -> str:
85
89
  Examples:
86
90
  OneDayCaseChar > one_day_case_char
87
91
  oneCamelCaseChar > one_camel_case_char
92
+ HTTPServer > http_server
88
93
  _TwoCamel_CaseChar > _two_camel_case_char
89
94
  _LeadingUnderscore_case_ > _leading_underscore_case_
90
95
  """
@@ -97,18 +102,28 @@ def camelcase_to_snakecase(input_string: str) -> str:
97
102
  snake_str += char
98
103
  continue
99
104
 
100
- if char.isupper() and index == 0:
101
- snake_str += char.lower()
102
- continue
103
-
104
- if char.isupper():
105
- if input_string[index - 1] != '_':
106
- snake_str += '_' + char.lower()
107
- else:
108
- snake_str += char.lower()
109
-
105
+ if not char.isupper():
106
+ snake_str += char
110
107
  continue
111
108
 
112
- snake_str += char
109
+ prev_char = input_string[index - 1] if index > 0 else ''
110
+ next_char = input_string[index + 1] if index + 1 < len(input_string) else ''
111
+
112
+ # Add underscore only at real word boundaries.
113
+ # This keeps acronyms together: HTTPServer -> http_server.
114
+ should_add_underscore = (
115
+ index > 0
116
+ and prev_char != '_'
117
+ and (
118
+ prev_char.islower()
119
+ or prev_char.isdigit()
120
+ or (prev_char.isupper() and next_char.islower())
121
+ )
122
+ )
123
+
124
+ if should_add_underscore:
125
+ snake_str += '_'
126
+
127
+ snake_str += char.lower()
113
128
 
114
129
  return snake_str
@@ -12,7 +12,7 @@ from backpack.logger import get_logger
12
12
  log = get_logger('Python Backpack - TestUtils')
13
13
 
14
14
 
15
- def random_string(length: str = 10) -> str:
15
+ def random_string(length: int = 10) -> str:
16
16
  """Generates a random string of fixed length.
17
17
 
18
18
  Args:
@@ -9,11 +9,12 @@
9
9
  # 1.1.2 07/2025 - Update actions versions, remove unused badge from README
10
10
  # 1.1.3 07/2025 - Improve test class names and docstrings for clarity, ruff formatting
11
11
  # 1.1.4 07/2025 - Moved from pipenv to poetry for dependency management.toml
12
+ # 2.0.0 03/2026 - Remove tox workflow and setup.py, standardize Poetry + coverage tooling
12
13
  # ----------------------------------------------------------------------------------------
13
14
 
14
- VERSION_MAJOR = 1
15
- VERSION_MINOR = 1
16
- VERSION_PATCH = 4
15
+ VERSION_MAJOR = 2
16
+ VERSION_MINOR = 0
17
+ VERSION_PATCH = 0
17
18
 
18
19
  version = f'{VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_PATCH}'
19
20
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-backpack"
3
- version = "1.1.4"
3
+ version = "2.0.0"
4
4
  description = "A collection of personal scripts for json, File/Folder Operations, String Validation, Custom Errors, Cache and stuff."
5
5
  authors = ["Maximiliano Rocamora"]
6
6
  packages = [
@@ -8,16 +8,15 @@ packages = [
8
8
  ]
9
9
 
10
10
  [tool.poetry.dependencies]
11
- python = "^3.9"
11
+ python = "^3.11"
12
12
 
13
13
  [tool.poetry.group.dev.dependencies]
14
- tox = "^4.0"
15
- ruff = "^0.0.289"
14
+ ruff = "^0.15.8"
16
15
  mock = "^5.2.0"
17
- coverage = "^7.9.2"
18
- pytest-cov = "^6.2.1"
19
- tox-gh-actions = "^3.3.0"
16
+ coverage = "^7.13.5"
17
+ pytest = "^8.4.2"
18
+ pytest-cov = "^7.1.0"
20
19
 
21
20
  [build-system]
22
- requires = ["poetry-core>=1.0.0"]
21
+ requires = ["poetry-core>=2.3.2"]
23
22
  build-backend = "poetry.core.masonry.api"
File without changes