python-ubercode-utils 2.0.2__py3-none-any.whl → 2.0.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-ubercode-utils
3
- Version: 2.0.2
3
+ Version: 2.0.4
4
4
  Summary: Core python utilities for all apps
5
5
  Home-page: https://github.com/sstacha/python-ubercode-utils
6
6
  Author: Steve Stacha
@@ -0,0 +1,14 @@
1
+ ubercode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ ubercode/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ ubercode/utils/convert.py,sha256=INxAIlIkwTeHSdVpqm8R_N5MzrSrjY9CduioHipu39g,11370
4
+ ubercode/utils/cursor.py,sha256=zHqbmUavCF0l7ck_Tu4PfgSzRO762ULKHYsnqa3r0uY,1118
5
+ ubercode/utils/data.py,sha256=DStj9BAXPSPvQRZwfEQVYt19EoMavyNFt11U8ZnhagE,4364
6
+ ubercode/utils/dataframe.py,sha256=3AYoZGZB7wtN5brBDfRgnuIaEubhRrSc7qx8hi_vaaE,1616
7
+ ubercode/utils/environment.py,sha256=97uYs0zGBkk7TfKuml6Th27kpdtYX_ILwfZTQW3Xac8,15818
8
+ ubercode/utils/logging.py,sha256=s4yIJWHo9MVO4oSk5IF3z6XTk-FOuyEiKnPFkiza3h4,9204
9
+ ubercode/utils/urls.py,sha256=N2B0s849WRpV_JWc20hBb4129UZZkeWEcPjfd9rggrY,13399
10
+ python_ubercode_utils-2.0.4.dist-info/LICENSE,sha256=lch0WEJaqJY3C5aCYSr0u6Gw0set96a2fp9ZWJRYuR8,1069
11
+ python_ubercode_utils-2.0.4.dist-info/METADATA,sha256=L5_YcnH89oIyTP_EeOLQR-AycJUR4YLHvEly5_RSRV0,1225
12
+ python_ubercode_utils-2.0.4.dist-info/WHEEL,sha256=hPN0AlP2dZM_3ZJZWP4WooepkmU9wzjGgCLCeFjkHLA,92
13
+ python_ubercode_utils-2.0.4.dist-info/top_level.txt,sha256=5BojzbvNCpPkFXcVQVr7cfovhbYQYAlMKgiHSMgi7VU,9
14
+ python_ubercode_utils-2.0.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.44.0)
2
+ Generator: bdist_wheel (0.46.3)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
ubercode/utils/convert.py CHANGED
@@ -13,7 +13,7 @@ FALSE_VALUES = [None, False, 0, "0", "n", "f", "false", "no", "off"]
13
13
 
14
14
 
15
15
  # -------- helper utilities ----------
16
- def strip(value: str or None, strip_chars: str = None, left: bool = True, right: bool = True) -> str or None:
16
+ def strip(value: str | None, strip_chars: str = None, left: bool = True, right: bool = True) -> str | None:
17
17
  """
18
18
  return stripped value if possible or original value
19
19
  :param value: str value to be stripped
@@ -94,7 +94,7 @@ def to_bool(value) -> bool:
94
94
  return bool(value)
95
95
 
96
96
 
97
- def is_true(value: int or bool or str) -> bool:
97
+ def is_true(value: int | bool | str) -> bool:
98
98
  """
99
99
  Convert <value> to a True boolean value. Useful when you want to convert a passed parameter to True if it matches
100
100
  one of the defined TRUE_VALUES above; otherwise False.
@@ -121,7 +121,7 @@ def to_js_bool(bool_value: bool) -> str:
121
121
  return "false"
122
122
 
123
123
 
124
- def to_int(value, default: int = 0, none_to_default: bool = True, suppress_warnings: bool = True) -> int or None:
124
+ def to_int(value, default: int | None = 0, none_to_default: bool = True, suppress_warnings: bool = True) -> int | None:
125
125
  """
126
126
  Convert <value> to int. Will always return integer or none instead of throwing exception
127
127
  @param value: value to be converted
@@ -201,7 +201,7 @@ def from_iso8601_compact(value: Any = None, tz: timezone = timezone.utc):
201
201
  return _value
202
202
 
203
203
 
204
- def to_date(value: Any = None, tz: timezone or None = timezone.utc, none_to_now: bool = True, suppress_warnings: bool = True):
204
+ def to_date(value: Any = None, tz: timezone | None = timezone.utc, none_to_now: bool = True, suppress_warnings: bool = True):
205
205
  """
206
206
  Convert string to python date. Currently, only concerned about iso8601 and db type formats.
207
207
  None returns current date by default but can be overridden with none_to_now optional parameter
@@ -232,7 +232,7 @@ def to_date(value: Any = None, tz: timezone or None = timezone.utc, none_to_now:
232
232
 
233
233
 
234
234
  # -------- helper conversions --------
235
- def to_mask(value: str or None) -> str or None:
235
+ def to_mask(value: str | None) -> str | None:
236
236
  _mask = value
237
237
  if isinstance(value, str) and value is not None and len(value) > 0:
238
238
  # if we are less than 4 chars then mask the entire string
@@ -7,8 +7,10 @@ import time
7
7
  from datetime import datetime
8
8
  from typing import Any, Tuple
9
9
  from pathlib import Path
10
+
10
11
  from ubercode.utils.logging import ColorLogger
11
12
  from ubercode.utils import convert
13
+ from ubercode.utils.urls import DjUrl
12
14
 
13
15
  _utils_settings_logger = ColorLogger("utils.environment")
14
16
 
@@ -225,6 +227,44 @@ class Environment:
225
227
  f"{db_parts[0]}[{db_parts[1]}][{db_parts[2]}] has a database or property naming issue!")
226
228
  return db_dict
227
229
 
230
+ def override_database_urls(self, db_dict: dict) -> dict:
231
+ """
232
+ Much like above,iterates over environment variables and overrides any database variables. However, instead
233
+ of looking for each variable with a pattern like DATABASES__default__ENGINE it looks for DJ_URL_ prefix and
234
+ parses the url into a DjUrl object. This allows property files to use one line per database config
235
+ instead of one key,value pair per variable like: DJ_URL_default = 'django.db.backends.mysql://scott:tiger@localhost:1366/test'
236
+ NOTE: you can omit everything except what you want to replace like: '://:newpassword@' which
237
+ will only replace the password
238
+
239
+ :param db_dict: settings database dictionary to replace variables in ex: DATABASES
240
+ :return: new dict with updated overridden values
241
+ """
242
+ items = []
243
+ if hasattr(self._env_map, "items"):
244
+ items = self._env_map.items()
245
+ elif hasattr(os.environ, "items"):
246
+ items = os.environ.items()
247
+ for k, v in items:
248
+ # unlike override_database_variables() we will look for prefix DJ_URL_
249
+ if k and str(k).upper().startswith('DJ_URL_') and v:
250
+ # the actual key is the remaining part left
251
+ key = str(k)[len('DJ_URL_'):]
252
+ djurl = DjUrl(str(v))
253
+ for attr, value in vars(djurl).items():
254
+ if value:
255
+ if not db_dict.get(key):
256
+ self._logger.warn(f'Missing database key [{key}]! creating...')
257
+ db_dict[key] = {}
258
+ _log_from_value = db_dict[key].get(attr.upper(), 'None')
259
+ _log_to_value = value
260
+ if attr.upper() in self._secret_properties:
261
+ _log_to_value = convert.to_mask(value)
262
+ db_dict[key][attr.upper()] = value
263
+ self._logger.info(
264
+ f'set [{key}][{attr.upper()}] from [{_log_from_value}] to [{_log_to_value}]')
265
+ return db_dict
266
+
267
+
228
268
  class FauxApp:
229
269
  def __init__(self, logger: ColorLogger = None, notebook_path: Path = Path(), default_dict: dict = None) -> None:
230
270
  self._logger = logger if logger else _utils_settings_logger
ubercode/utils/urls.py CHANGED
@@ -1,8 +1,8 @@
1
1
  import os
2
2
  from urllib.parse import urlsplit
3
- from ubercode.utils.convert import to_str
3
+ from ubercode.utils.convert import to_str, to_int
4
4
  from pathlib import PurePath
5
-
5
+ from .convert import to_mask
6
6
 
7
7
  class ParsedQueryString:
8
8
  """
@@ -216,6 +216,99 @@ class ParsedUrl:
216
216
  """
217
217
  return self.url
218
218
 
219
+ class DjUrl:
220
+ engine = None
221
+ user = None
222
+ password = None
223
+ host = None
224
+ port = None
225
+ name = None
226
+
227
+ def __init__(self, dj_url: str = None) -> None:
228
+ """
229
+ parses a packed "django_url" into its parts following similar rules to SqlAlchemy
230
+ format: engine://user:password@host[:port]/dbname
231
+ example: django.db.backends.mysql://scott:tiger@localhost:1366/test
232
+
233
+ NOTE: asking for the string value will give back the original packed url masking the password
234
+ NOTE: to_dict will give back the dictionary values to be added or replaced in the DATABASES dict
235
+
236
+ :param url: packed django url ex: django.db.backends.mysql://scott:tiger@localhost:1366/test
237
+ """
238
+ dj_url = dj_url or ""
239
+ dj_url = dj_url.strip()
240
+ encoded = False
241
+ if dj_url.endswith('?--atencoded'):
242
+ encoded = True
243
+ dj_url = dj_url[:-len('?--atencoded')].strip()
244
+ if not dj_url:
245
+ return
246
+ pos = dj_url.find('://')
247
+ if pos > -1:
248
+ self.engine = dj_url[:pos].strip() or None
249
+ constr = dj_url[pos + len('://'):].strip()
250
+ else:
251
+ constr = dj_url
252
+ pos = constr.find("@")
253
+ if pos > -1:
254
+ loginstr = constr[:pos].strip()
255
+ constr = constr[pos + len("@"):].strip()
256
+ pos = loginstr.find(':')
257
+ if pos > -1:
258
+ self.user = loginstr[:pos].strip() or None
259
+ self.password = loginstr[pos + len(':'):].strip() or None
260
+ if encoded:
261
+ self.password = self.password.replace('%40', '@')
262
+ elif len(loginstr) > 0:
263
+ self.user = loginstr or None
264
+ else:
265
+ # since password is the most common replacement look for that specifically next if we didn't have an @
266
+ # NOTE: since password can contain / we will assume the only thing there is the password otherwise use @
267
+ # Ex: DjUrl(':newpassword/newdatabase') -> password=newpassword/newdatabase name=None
268
+ # DjUrl(':newpassword@/newdatabase') -> password=newpassword name=newdatabase
269
+ # DjUrl(':asfcasdf23%401:/!?--atencoded') -> password=asfcasdf23@1:/! name=None port=None
270
+ if constr.startswith(':'):
271
+ self.password = constr.strip(':')
272
+ constr = ''
273
+ if encoded:
274
+ self.password = self.password.replace('%40', '@')
275
+ # NOTE: constr now contains everything after @ - no engine, user, password
276
+ # NOTE: may have port but no db ex: @:8080
277
+ pos = constr.find('/')
278
+ if pos > -1:
279
+ hoststr = constr[:pos].strip()
280
+ self.name = constr[pos + len('/'):].strip() or None
281
+ else:
282
+ hoststr = constr
283
+ # all that is left is hoststr
284
+ pos = hoststr.find(':')
285
+ if pos > -1:
286
+ self.host = hoststr[:pos].strip() or None
287
+ self.port = hoststr[pos + len(':'):].strip() or None
288
+ if self.port:
289
+ self.port = to_int(self.port, default=None, none_to_default=False)
290
+ elif len(hoststr) > 0:
291
+ self.host = hoststr
292
+
293
+ def to_dict(self) -> dict:
294
+ dct = {}
295
+ for attr, value in vars(self).items():
296
+ dct[attr.upper()] = value
297
+ return dct
298
+
299
+ def __str__(self) -> str:
300
+ url = f"{self.engine or ''}://"
301
+ if self.user:
302
+ url += self.user
303
+ if self.password:
304
+ url += f":{to_mask(self.password)}"
305
+ url += f"@{self.host or ''}"
306
+ if self.port:
307
+ url += f":{self.port}"
308
+ if self.name:
309
+ url += f"/{self.name}"
310
+ return url
311
+
219
312
 
220
313
  if __name__ == "__main__":
221
314
  # test_uri = "http://localhost:8000/test1/?id=1&x=2"
@@ -1,14 +0,0 @@
1
- ubercode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- ubercode/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- ubercode/utils/convert.py,sha256=YWIacPg3MaUTGSANZ2yzxanIkUCH2oyqOR6Wvtz91Fg,11371
4
- ubercode/utils/cursor.py,sha256=zHqbmUavCF0l7ck_Tu4PfgSzRO762ULKHYsnqa3r0uY,1118
5
- ubercode/utils/data.py,sha256=DStj9BAXPSPvQRZwfEQVYt19EoMavyNFt11U8ZnhagE,4364
6
- ubercode/utils/dataframe.py,sha256=3AYoZGZB7wtN5brBDfRgnuIaEubhRrSc7qx8hi_vaaE,1616
7
- ubercode/utils/environment.py,sha256=zSgemMspWx1mBPeI2CSStCSj_kjVm_rEVZaNRkEMsZM,13657
8
- ubercode/utils/logging.py,sha256=s4yIJWHo9MVO4oSk5IF3z6XTk-FOuyEiKnPFkiza3h4,9204
9
- ubercode/utils/urls.py,sha256=PaksDHVg_aSXEqcN4SlLgY-erqeMCNw8mYK9qt3q05I,9652
10
- python_ubercode_utils-2.0.2.dist-info/LICENSE,sha256=lch0WEJaqJY3C5aCYSr0u6Gw0set96a2fp9ZWJRYuR8,1069
11
- python_ubercode_utils-2.0.2.dist-info/METADATA,sha256=Oe6G8phzfGZ6SFVZdd7FDgsNGjjF999kF4e70dU4dEE,1225
12
- python_ubercode_utils-2.0.2.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
13
- python_ubercode_utils-2.0.2.dist-info/top_level.txt,sha256=5BojzbvNCpPkFXcVQVr7cfovhbYQYAlMKgiHSMgi7VU,9
14
- python_ubercode_utils-2.0.2.dist-info/RECORD,,