python-sdk-remote 0.0.190b3356__tar.gz → 0.0.193b3590__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 (41) hide show
  1. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/PKG-INFO +1 -1
  2. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/__init__.py +2 -2
  3. python_sdk_remote-0.0.190b3356/python_sdk_remote/src/our_environment_variable_exception.py → python_sdk_remote-0.0.193b3590/python_sdk_remote/src/environment_variable_exception.py +1 -5
  4. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/unified_json.py +2 -6
  5. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/utilities.py +127 -28
  6. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote.egg-info/PKG-INFO +1 -1
  7. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote.egg-info/SOURCES.txt +0 -8
  8. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/setup.py +2 -2
  9. python_sdk_remote-0.0.190b3356/python_sdk_remote/src/dot_env_files.py +0 -80
  10. python_sdk_remote-0.0.190b3356/python_sdk_remote/src/environment.py +0 -21
  11. python_sdk_remote-0.0.190b3356/python_sdk_remote/src/environment_variable_exception.py +0 -2
  12. python_sdk_remote-0.0.190b3356/python_sdk_remote/src/filesystem.py +0 -15
  13. python_sdk_remote-0.0.190b3356/python_sdk_remote/src/masking.py +0 -8
  14. python_sdk_remote-0.0.190b3356/python_sdk_remote/src/our_environment_variable.py +0 -96
  15. python_sdk_remote-0.0.190b3356/python_sdk_remote/src/personal_repo.py +0 -31
  16. python_sdk_remote-0.0.190b3356/python_sdk_remote/src/username.py +0 -11
  17. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/README.md +0 -0
  18. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/pyproject.toml +0 -0
  19. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/constants.py +0 -0
  20. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/constants_src_mini_logger_and_logger.py +0 -0
  21. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/datetime_range.py +0 -0
  22. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/filename.py +0 -0
  23. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/generic_crud.py +0 -0
  24. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/http_response.py +0 -0
  25. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/is_test_data.py +0 -0
  26. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/item.py +0 -0
  27. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/mini_logger.py +0 -0
  28. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/our_obfuscation.py +0 -0
  29. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/our_object.py +0 -0
  30. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/our_objects.py +0 -0
  31. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/our_objects_local.py +0 -0
  32. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/our_objects_remote.py +0 -0
  33. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/our_request.py +0 -0
  34. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/our_request_old.py +0 -0
  35. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/valid_json_versions.py +0 -0
  36. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/validate_environment.py +0 -0
  37. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote/src/version.py +0 -0
  38. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote.egg-info/dependency_links.txt +0 -0
  39. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote.egg-info/requires.txt +0 -0
  40. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/python_sdk_remote.egg-info/top_level.txt +0 -0
  41. {python_sdk_remote-0.0.190b3356 → python_sdk_remote-0.0.193b3590}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-sdk-remote
3
- Version: 0.0.190b3356
3
+ Version: 0.0.193b3590
4
4
  Summary: PyPI Package for Circles Python SDK Local Python
5
5
  Home-page: https://github.com/circles-zone/python-sdk-remote-python-package
6
6
  Author: Circles
@@ -10,7 +10,7 @@ from .utilities import (
10
10
  get_environment_name,
11
11
  get_brand_name,
12
12
  our_get_env,
13
- OurEnvironmentVariableException,
13
+ EnvironmentVariableException,
14
14
  our_set_env,
15
15
  our_delete_env,
16
16
  to_dict,
@@ -83,7 +83,7 @@ __all__ = [
83
83
  'get_environment_name',
84
84
  'get_brand_name',
85
85
  'our_get_env',
86
- 'OurEnvironmentVariableException',
86
+ 'EnvironmentVariableException',
87
87
  'our_set_env',
88
88
  'our_delete_env',
89
89
  'to_dict',
@@ -1,10 +1,6 @@
1
- class OurEnvironmentVariableException(Exception):
1
+ class EnvironmentVariableException(Exception):
2
2
  """Raised when a required environment variable is missing or empty.
3
3
 
4
4
  A specific exception (rather than a generic Exception) so callers can catch
5
5
  exactly this case; shared by our_get_env / our_delete_env for consistency.
6
6
  """
7
-
8
-
9
- # Backward compatibility: the name before the Our prefix
10
- EnvironmentVariableException = OurEnvironmentVariableException
@@ -5,8 +5,9 @@ from .valid_json_versions import valid_json_versions
5
5
  # Alias validate_json
6
6
  class UnifiedJson:
7
7
  def __init__(self, data: dict, json_version: str):
8
+ # TODO Move this code to UnifiedJson.is_valid_json() method and call it
8
9
  # TODO Write function/method both in TypeScript and Python to validate JSON i.e. to make sure there is version field in the upper level (Like in AWS JSON)
9
- if not self.is_valid_json(json_version):
10
+ if json_version not in valid_json_versions:
10
11
  raise Exception(
11
12
  f"version {json_version} is not in valid_json_versions {valid_json_versions}, "
12
13
  f"please make sure you run sql2code."
@@ -14,11 +15,6 @@ class UnifiedJson:
14
15
  self.json_version = json_version
15
16
  self.data = data
16
17
 
17
- @staticmethod
18
- def is_valid_json(json_version: str) -> bool:
19
- is_valid_json_result = json_version in valid_json_versions
20
- return is_valid_json_result
21
-
22
18
  def get_unified_json(self):
23
19
  get_unified_json_result = {"version": self.json_version, "data": self.data}
24
20
  return get_unified_json_result
@@ -17,18 +17,7 @@ from .version import PACKAGE_VERSION
17
17
 
18
18
  from .mini_logger import MiniLogger as logger
19
19
 
20
- from .our_environment_variable_exception import EnvironmentVariableException, OurEnvironmentVariableException # noqa: F401
21
- # Split out of this file, imported here so utilities.<name> keeps working
22
- from .dot_env_files import ( # noqa: F401
23
- DOT_ENV_FILENAME, _get_env_or_dot_env, add_to_file_if_not_existed, get_dot_env_file_value,
24
- get_our_dot_env_filenames, update_dot_env_example)
25
- from .environment import get_environment_name # noqa: F401
26
- from .filesystem import _find_nearest_file # noqa: F401
27
- from .masking import _mask # noqa: F401
28
- from .our_environment_variable import our_delete_env, our_get_env, our_set_env # noqa: F401
29
- from .personal_repo import ( # noqa: F401
30
- PERSONAL_REPO_DIRECTORY_SUFFIX, PERSONAL_REPO_DOT_ENV_DIRECTORY, get_personal_repo_path)
31
- from .username import _get_username_from_full_name # noqa: F401
20
+ from .environment_variable_exception import EnvironmentVariableException
32
21
 
33
22
  load_dotenv()
34
23
  # Load environment variables from .env file
@@ -138,33 +127,31 @@ def is_valid_time_range(time_range: tuple) -> bool:
138
127
 
139
128
 
140
129
  # Enhanced URL validation with support for both str and URL types
141
-
142
-
143
130
  def validate_url(url: Union[str, object]) -> bool:
144
131
  """
145
132
  Validate URL format.
146
-
133
+
147
134
  Args:
148
135
  url: URL string or URL object to validate
149
-
136
+
150
137
  Returns:
151
138
  bool: True if URL is valid, False otherwise
152
139
  """
153
140
  logger.start(object={"url": url, "url_type": type(url).__name__})
154
-
141
+
155
142
  # Handle URL object (if using yarl or similar library)
156
143
  if hasattr(url, 'scheme') and hasattr(url, 'host'):
157
144
  is_valid_url = bool(url.scheme and url.host)
158
145
  logger.end(object={"is_valid_url": is_valid_url, "source": "url_object"})
159
146
  return is_valid_url
160
-
147
+
161
148
  # Handle string URL
162
149
  if url is not None and url != "":
163
150
  parsed_url = urlparse(str(url))
164
151
  is_valid_url = bool(parsed_url.scheme and parsed_url.netloc)
165
152
  else:
166
153
  is_valid_url = True # Empty URL is considered valid (optional field)
167
-
154
+
168
155
  logger.end(object={"is_valid_url": is_valid_url, "source": "string_parsing"})
169
156
  return is_valid_url
170
157
 
@@ -319,6 +306,110 @@ def is_datetime_in_datetime_range(check_datetime: datetime, datetime_range: Tupl
319
306
  return is_datetime_in_datetime_range_result
320
307
 
321
308
 
309
+ # TODO shall we rename the function _file_ to _key_value_file_
310
+ def add_to_file_if_not_existed(file: str, key: str, value: Optional[str]) -> None:
311
+ logger.start(object={"file": file, "key": key, "value": value})
312
+ if not os.path.exists(file):
313
+ with open(file, "w") as f:
314
+ f.write(f"{key}={value}\n")
315
+ else:
316
+ with open(file, "r") as f:
317
+ lines = f.readlines()
318
+ if f"{key}=" not in lines:
319
+ with open(file, "a") as f:
320
+ f.write(f"{key}={value}\n")
321
+ logger.end()
322
+
323
+
324
+ def update_dot_env_example(key: str, value: Optional[str]):
325
+ # Avoid recursion by checking environment directly
326
+ env_name = os.getenv("ENVIRONMENT_NAME", "").lower()
327
+ if env_name == "play1":
328
+ return
329
+ add_to_file_if_not_existed(file=".env.example", key=key, value=value)
330
+
331
+
332
+ # TODO If running in a lower environment(i.e. local1, play1) if key do not exit in Personal Repo relevant .env.{environment_} or value diffrent update Personal Repo (create new and comment the old record)
333
+ # TODO If running in a lower environment(i.e. local1, play1) update .env.example with the key without the value
334
+ # TODO If running in a lower environment(i.e. local1, play1) and the key do not exist look for it in the Team Member Personal Repo and copy both the key and value to the repo .env.{environment_name} file
335
+ # TODO move all our_*_env() to our_env.py
336
+ # TODO shall we rename optional_prefix to prefix?
337
+ def our_get_env(key: str, default: Optional[str] = None, raise_if_not_found: bool = True,
338
+ raise_if_empty: bool = False, optional_prefix: Optional[str] = None) -> Optional[str]:
339
+ """
340
+ Get environment variable with optional prefix fallback.
341
+
342
+ Args:
343
+ key: Environment variable name
344
+ default: Default value if not found
345
+ raise_if_not_found: Raise exception if variable not found and no default
346
+ raise_if_empty: Raise exception if variable is empty
347
+ optional_prefix: Try with this prefix first, then without it
348
+
349
+ Returns:
350
+ Environment variable value
351
+ """
352
+ logger.start(object={"key": key, "default": default,
353
+ "raise_if_not_found": raise_if_not_found,
354
+ "raise_if_empty": raise_if_empty,
355
+ "optional_prefix": optional_prefix})
356
+
357
+ # Try with prefix first if provided
358
+ if optional_prefix:
359
+ prefixed_key = f"{optional_prefix}_{key}"
360
+ env_var = os.getenv(prefixed_key)
361
+ if env_var is not None:
362
+ logger.end(object={"env_var": env_var, "used_prefix": True})
363
+ return env_var.strip('"\'') if isinstance(env_var, str) else env_var
364
+
365
+ # Try without prefix
366
+ env_var = os.getenv(key, default)
367
+
368
+ if ((raise_if_not_found and key not in os.environ and default is None)
369
+ or (raise_if_empty and not env_var and not default)):
370
+ raise EnvironmentVariableException(f"Environment variable {key} not found - please check your .env file") # noqa: E501
371
+
372
+ # Strip quotes from env_var if present
373
+ if env_var and isinstance(env_var, str):
374
+ env_var = env_var.strip('"\'')
375
+
376
+ update_dot_env_example(key, value=default)
377
+ logger.end(object={"env_var": env_var})
378
+ return env_var
379
+
380
+
381
+ def our_set_env(key: str, value: str) -> None:
382
+ """
383
+ Set an environment variable, routed through the logger so the system can
384
+ track which repos/tests write which env vars (cf. our_get_env).
385
+
386
+ Args:
387
+ key: Environment variable name
388
+ value: Value to set
389
+ """
390
+ logger.start(object={"key": key, "value": value})
391
+ os.environ[key] = value
392
+ logger.end(object={"key": key})
393
+
394
+
395
+ def our_delete_env(key: str, raise_if_not_found: bool = False) -> None:
396
+ """
397
+ Delete an environment variable if present, routed through the logger
398
+ (cf. our_get_env / our_set_env).
399
+
400
+ Args:
401
+ key: Environment variable name
402
+ raise_if_not_found: Raise if the variable is not set
403
+ """
404
+ logger.start(object={"key": key,
405
+ "raise_if_not_found": raise_if_not_found})
406
+ if key in os.environ:
407
+ del os.environ[key]
408
+ elif raise_if_not_found:
409
+ raise EnvironmentVariableException(f"Environment variable {key} not found") # noqa: E501
410
+ logger.end(object={"key": key})
411
+
412
+
322
413
  def get_brand_name(raise_if_not_found: bool = True,
323
414
  raise_if_empty: bool = False) -> Optional[str]:
324
415
  get_brand_name_result = our_get_env("BRAND_NAME",
@@ -327,6 +418,19 @@ def get_brand_name(raise_if_not_found: bool = True,
327
418
  return get_brand_name_result
328
419
 
329
420
 
421
+ def get_environment_name(raise_if_not_found: bool = True,
422
+ raise_if_empty: bool = False) -> Optional[str]:
423
+ environment_name = our_get_env("ENVIRONMENT_NAME",
424
+ raise_if_not_found=raise_if_not_found,
425
+ raise_if_empty=raise_if_empty)
426
+ try:
427
+ EnvironmentName(environment_name) # if invalid, raises ValueError: x is not a valid EnvironmentName
428
+ except ValueError:
429
+ # Shows the current PACKAGE_VERSION for debugging
430
+ raise Exception(f"python-sdk-remote {PACKAGE_VERSION} get_environment_name(): Invalid EnvironmentName: {environment_name} either misspelling or you need the environment in a few places such as url-remote-python-package environment_name_enum.py ...")
431
+ return environment_name
432
+
433
+
330
434
  def get_product_user_identifier(raise_if_not_found: bool = True,
331
435
  raise_if_empty: bool = False) -> Optional[str]:
332
436
  get_product_user_identifier_result = our_get_env("PRODUCT_USER_IDENTIFIER",
@@ -339,7 +443,7 @@ def get_product_user_password(raise_if_not_found: bool = True,
339
443
  raise_if_empty: bool = False) -> Optional[str]:
340
444
  """
341
445
  Get product user password from environment variables.
342
-
446
+
343
447
  Note: PRODUCT_USER_PASSWORD is preferred over PRODUCT_PASSWORD (deprecated).
344
448
  This function maintains backward compatibility.
345
449
  """
@@ -357,8 +461,6 @@ def get_product_user_password(raise_if_not_found: bool = True,
357
461
 
358
462
 
359
463
  # Database-specific functions - Consider moving to database package when it's created
360
-
361
-
362
464
  def get_sql_hostname(raise_if_not_found: bool = True,
363
465
  raise_if_empty: bool = False) -> Optional[str]:
364
466
  get_sql_hostname_result = our_get_env("RDS_HOSTNAME",
@@ -379,8 +481,6 @@ def get_sql_password(raise_if_empty: bool = False) -> Optional[str]:
379
481
 
380
482
 
381
483
  # Dialog-specific function - Consider moving to dialog repo when it's created
382
-
383
-
384
484
  def get_dialog_jwt_secret_key() -> Optional[str]:
385
485
  get_dialog_jwt_secret_key_result = our_get_env("DIALOG_JWT_SECRET_KEY", raise_if_empty=True)
386
486
  return get_dialog_jwt_secret_key_result
@@ -407,13 +507,13 @@ def decode_jwt(token: str, key: str) -> dict:
407
507
  def obfuscate_log_dict(log_dict: dict) -> dict:
408
508
  """
409
509
  Obfuscate sensitive keys in log dictionaries for security.
410
-
510
+
411
511
  Obfuscates keys containing:
412
512
  - password, secret, token, jwt
413
513
  - email (with variations like e-mail, e_mail)
414
514
  - phone, name (first/last/nick/user etc.)
415
515
  - address, ssn
416
-
516
+
417
517
  In play1 environment, returns original dict for debugging purposes.
418
518
  """
419
519
  environment_name = get_environment_name()
@@ -433,7 +533,6 @@ def obfuscate_log_dict(log_dict: dict) -> dict:
433
533
 
434
534
  # Tests for the following functions are implemented in utilities_test.py
435
535
 
436
-
437
536
  def remove_digits(text: str) -> str:
438
537
  remove_digits_result = ''.join(i for i in text if not i.isdigit())
439
538
  return remove_digits_result
@@ -489,7 +588,7 @@ def validate_arguments(args: dict) -> None:
489
588
  def reformat_time_string(input_str: str) -> str:
490
589
  """
491
590
  Reformat time string from compact format to HH:MM:SS:MS format.
492
-
591
+
493
592
  Example:
494
593
  "1234" -> "12:34:00:00"
495
594
  "123456" -> "12:34:56:00"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-sdk-remote
3
- Version: 0.0.190b3356
3
+ Version: 0.0.193b3590
4
4
  Summary: PyPI Package for Circles Python SDK Local Python
5
5
  Home-page: https://github.com/circles-zone/python-sdk-remote-python-package
6
6
  Author: Circles
@@ -10,19 +10,13 @@ python_sdk_remote/src/__init__.py
10
10
  python_sdk_remote/src/constants.py
11
11
  python_sdk_remote/src/constants_src_mini_logger_and_logger.py
12
12
  python_sdk_remote/src/datetime_range.py
13
- python_sdk_remote/src/dot_env_files.py
14
- python_sdk_remote/src/environment.py
15
13
  python_sdk_remote/src/environment_variable_exception.py
16
14
  python_sdk_remote/src/filename.py
17
- python_sdk_remote/src/filesystem.py
18
15
  python_sdk_remote/src/generic_crud.py
19
16
  python_sdk_remote/src/http_response.py
20
17
  python_sdk_remote/src/is_test_data.py
21
18
  python_sdk_remote/src/item.py
22
- python_sdk_remote/src/masking.py
23
19
  python_sdk_remote/src/mini_logger.py
24
- python_sdk_remote/src/our_environment_variable.py
25
- python_sdk_remote/src/our_environment_variable_exception.py
26
20
  python_sdk_remote/src/our_obfuscation.py
27
21
  python_sdk_remote/src/our_object.py
28
22
  python_sdk_remote/src/our_objects.py
@@ -30,9 +24,7 @@ python_sdk_remote/src/our_objects_local.py
30
24
  python_sdk_remote/src/our_objects_remote.py
31
25
  python_sdk_remote/src/our_request.py
32
26
  python_sdk_remote/src/our_request_old.py
33
- python_sdk_remote/src/personal_repo.py
34
27
  python_sdk_remote/src/unified_json.py
35
- python_sdk_remote/src/username.py
36
28
  python_sdk_remote/src/utilities.py
37
29
  python_sdk_remote/src/valid_json_versions.py
38
30
  python_sdk_remote/src/validate_environment.py
@@ -8,9 +8,9 @@ package_dir = PACKAGE_NAME.replace("-", "_")
8
8
  setuptools.setup(
9
9
  name=PACKAGE_NAME,
10
10
  # Every feature branch needs its own free beta: PyPI never accepts a version twice, and
11
- # dev's literal is always one that has already been published. b3517 is this branch's
11
+ # dev's literal is always one that has already been published. b3590 is this branch's
12
12
  # Jira id. GA stays a [pub] commit on dev.
13
- version='0.0.190b3356', # https://pypi.org/project/python-sdk-remote#history
13
+ version='0.0.193b3590', # https://pypi.org/project/python-sdk-remote#history
14
14
  author="Circles",
15
15
  author_email="info@circlez.ai",
16
16
  description="PyPI Package for Circles Python SDK Local Python",
@@ -1,80 +0,0 @@
1
- import os
2
- from typing import Optional
3
-
4
- from dotenv import dotenv_values
5
-
6
- from .filesystem import _find_nearest_file
7
- from .masking import _mask
8
- from .mini_logger import MiniLogger as logger
9
- from .personal_repo import PERSONAL_REPO_DOT_ENV_DIRECTORY, get_personal_repo_path
10
-
11
- DOT_ENV_FILENAME = ".env"
12
-
13
-
14
- def get_dot_env_file_value(dot_env_filename: str, key: str) -> Optional[str]:
15
- """The value of key in a .env file, None when the file or the key is missing"""
16
- if not os.path.isfile(dot_env_filename):
17
- return None
18
- return dotenv_values(dot_env_filename).get(key)
19
-
20
-
21
- def get_our_dot_env_filenames(start_directory: Optional[str] = None, environment_name: Optional[str] = None,
22
- personal_repo_path: Optional[str] = None) -> list:
23
- """The .env files our_get_env() looks in, in this order: the project's .env.<ENVIRONMENT_NAME> and .env (the nearest
24
- ones from the current directory up), then the personal repo's .config/circlez.ai/.env.<ENVIRONMENT_NAME> and
25
- .config/circlez.ai/.env"""
26
- # Not get_environment_name(), which calls our_get_env()
27
- environment_name = environment_name or os.environ.get("ENVIRONMENT_NAME")
28
- dot_env_basenames = ([f"{DOT_ENV_FILENAME}.{environment_name}"] if environment_name else []) + [DOT_ENV_FILENAME]
29
- dot_env_filenames = []
30
- for dot_env_basename in dot_env_basenames:
31
- dot_env_filename = _find_nearest_file(dot_env_basename, start_directory)
32
- if dot_env_filename:
33
- dot_env_filenames.append(dot_env_filename)
34
- personal_repo_path = personal_repo_path or get_personal_repo_path()
35
- if personal_repo_path:
36
- for dot_env_basename in dot_env_basenames:
37
- dot_env_filename = os.path.join(personal_repo_path, PERSONAL_REPO_DOT_ENV_DIRECTORY, dot_env_basename)
38
- if os.path.isfile(dot_env_filename) and dot_env_filename not in dot_env_filenames:
39
- dot_env_filenames.append(dot_env_filename)
40
- return dot_env_filenames
41
-
42
-
43
- def _get_env_or_dot_env(key: str) -> Optional[str]:
44
- """The environment variable, otherwise its value in the .env files of get_our_dot_env_filenames(), which is then
45
- set in the environment so the next lookup does not read the files again"""
46
- env_var = os.getenv(key)
47
- if env_var is not None:
48
- return env_var
49
- for dot_env_filename in get_our_dot_env_filenames():
50
- dot_env_value = get_dot_env_file_value(dot_env_filename, key)
51
- if dot_env_value:
52
- os.environ[key] = dot_env_value
53
- logger.info(f"our_get_env() found {key}={_mask(dot_env_value)} in {dot_env_filename}")
54
- return dot_env_value
55
- return None
56
-
57
-
58
- # TODO shall we rename the function _file_ to _key_value_file_
59
-
60
-
61
- def add_to_file_if_not_existed(file: str, key: str, value: Optional[str]) -> None:
62
- logger.start(object={"file": file, "key": key, "value": value})
63
- if not os.path.exists(file):
64
- with open(file, "w") as f:
65
- f.write(f"{key}={value}\n")
66
- else:
67
- with open(file, "r") as f:
68
- lines = f.readlines()
69
- if f"{key}=" not in lines:
70
- with open(file, "a") as f:
71
- f.write(f"{key}={value}\n")
72
- logger.end()
73
-
74
-
75
- def update_dot_env_example(key: str, value: Optional[str]):
76
- # Avoid recursion by checking environment directly
77
- env_name = os.getenv("ENVIRONMENT_NAME", "").lower()
78
- if env_name == "play1":
79
- return
80
- add_to_file_if_not_existed(file=".env.example", key=key, value=value)
@@ -1,21 +0,0 @@
1
- from typing import Optional
2
-
3
- from url_remote.environment_name_enum import EnvironmentName
4
-
5
- from .our_environment_variable import our_get_env
6
- from .version import PACKAGE_VERSION
7
-
8
-
9
- def get_environment_name(raise_if_not_found: bool = True,
10
- raise_if_empty: bool = False) -> Optional[str]:
11
- environment_name = our_get_env("ENVIRONMENT_NAME",
12
- raise_if_not_found=raise_if_not_found,
13
- raise_if_empty=raise_if_empty)
14
- try:
15
- EnvironmentName(environment_name) # if invalid, raises ValueError: x is not a valid EnvironmentName
16
- except ValueError:
17
- # Shows the current PACKAGE_VERSION for debugging
18
- raise Exception(f"python-sdk-remote {PACKAGE_VERSION} get_environment_name(): Invalid EnvironmentName: "
19
- f"{environment_name} either misspelling or you need the environment in a few places such as "
20
- "url-remote-python-package environment_name_enum.py ...")
21
- return environment_name
@@ -1,2 +0,0 @@
1
- # Backward compatibility: the module before it was renamed to our_environment_variable_exception.py
2
- from .our_environment_variable_exception import EnvironmentVariableException, OurEnvironmentVariableException # noqa: F401
@@ -1,15 +0,0 @@
1
- import os
2
- from typing import Optional
3
-
4
-
5
- def _find_nearest_file(filename: str, start_directory: Optional[str] = None) -> Optional[str]:
6
- """The nearest filename from start_directory (default: the current directory) up, as python-dotenv finds .env"""
7
- directory = os.path.abspath(start_directory or os.getcwd())
8
- while True:
9
- candidate = os.path.join(directory, filename)
10
- if os.path.isfile(candidate):
11
- return candidate
12
- parent_directory = os.path.dirname(directory)
13
- if parent_directory == directory:
14
- return None
15
- directory = parent_directory
@@ -1,8 +0,0 @@
1
- from typing import Optional
2
-
3
-
4
- def _mask(value: Optional[str]) -> str:
5
- # Same as PowerShell Masking.psm1 Get-MaskedToken: environment variables are often secrets
6
- if not value:
7
- return "<empty>"
8
- return "****" if len(value) <= 8 else "****" + value[-4:]
@@ -1,96 +0,0 @@
1
- import os
2
- from typing import Optional
3
-
4
- from .dot_env_files import _get_env_or_dot_env, update_dot_env_example
5
- from .masking import _mask
6
- from .mini_logger import MiniLogger as logger
7
- from .our_environment_variable_exception import OurEnvironmentVariableException
8
-
9
- # TODO If running in a lower environment(i.e. local1, play1) if key do not exit in Personal Repo relevant
10
- # .env.{environment_} or value diffrent update Personal Repo (create new and comment the old record)
11
- # TODO If running in a lower environment(i.e. local1, play1) update .env.example with the key without the value
12
- # TODO If running in a lower environment(i.e. local1, play1) and the key do not exist look for it in the Team Member
13
- # Personal Repo and copy both the key and value to the repo .env.{environment_name} file
14
- # TODO move all our_*_env() to our_env.py
15
- # TODO shall we rename optional_prefix to prefix?
16
-
17
-
18
- def our_get_env(key: str, default: Optional[str] = None, raise_if_not_found: bool = True,
19
- raise_if_empty: bool = False, optional_prefix: Optional[str] = None) -> Optional[str]:
20
- """
21
- Get environment variable with optional prefix fallback.
22
-
23
- Args:
24
- key: Environment variable name
25
- default: Default value if not found
26
- raise_if_not_found: Raise exception if variable not found and no default
27
- raise_if_empty: Raise exception if variable is empty
28
- optional_prefix: Try with this prefix first, then without it
29
-
30
- Returns:
31
- Environment variable value
32
- """
33
- logger.start(object={"key": key, "default": default,
34
- "raise_if_not_found": raise_if_not_found,
35
- "raise_if_empty": raise_if_empty,
36
- "optional_prefix": optional_prefix})
37
-
38
- # A key missing in the environment is looked for in the project's .env.<ENVIRONMENT_NAME> and .env, then in the
39
- # personal repo's .config/circlez.ai/.env.<ENVIRONMENT_NAME> and .env (as PowerShell Get-OurEnvironmentVariable and
40
- # TypeScript ourGetEnv)
41
- # Try with prefix first if provided
42
- if optional_prefix:
43
- prefixed_key = f"{optional_prefix}_{key}"
44
- env_var = _get_env_or_dot_env(prefixed_key)
45
- if env_var is not None:
46
- logger.end(object={"env_var": _mask(env_var), "used_prefix": True})
47
- return env_var.strip('"\'') if isinstance(env_var, str) else env_var
48
-
49
- # Try without prefix
50
- env_var = _get_env_or_dot_env(key)
51
- if env_var is None:
52
- env_var = default
53
-
54
- if ((raise_if_not_found and key not in os.environ and default is None)
55
- or (raise_if_empty and not env_var and not default)):
56
- raise OurEnvironmentVariableException(f"Environment variable {key} not found - please check your .env file") # noqa: E501
57
-
58
- # Strip quotes from env_var if present
59
- if env_var and isinstance(env_var, str):
60
- env_var = env_var.strip('"\'')
61
-
62
- update_dot_env_example(key, value=default)
63
- logger.end(object={"env_var": _mask(env_var)})
64
- return env_var
65
-
66
-
67
- def our_set_env(key: str, value: str) -> None:
68
- """
69
- Set an environment variable, routed through the logger so the system can
70
- track which repos/tests write which env vars (cf. our_get_env).
71
-
72
- Args:
73
- key: Environment variable name
74
- value: Value to set
75
- """
76
- logger.start(object={"key": key, "value": value})
77
- os.environ[key] = value
78
- logger.end(object={"key": key})
79
-
80
-
81
- def our_delete_env(key: str, raise_if_not_found: bool = False) -> None:
82
- """
83
- Delete an environment variable if present, routed through the logger
84
- (cf. our_get_env / our_set_env).
85
-
86
- Args:
87
- key: Environment variable name
88
- raise_if_not_found: Raise if the variable is not set
89
- """
90
- logger.start(object={"key": key,
91
- "raise_if_not_found": raise_if_not_found})
92
- if key in os.environ:
93
- del os.environ[key]
94
- elif raise_if_not_found:
95
- raise OurEnvironmentVariableException(f"Environment variable {key} not found") # noqa: E501
96
- logger.end(object={"key": key})
@@ -1,31 +0,0 @@
1
- import os
2
- from typing import Optional
3
-
4
- from .username import _get_username_from_full_name
5
-
6
- PERSONAL_REPO_DIRECTORY_SUFFIX = "-circlez.ai"
7
- # The directory of the .env files in the personal repo
8
- PERSONAL_REPO_DOT_ENV_DIRECTORY = os.path.join(".config", "circlez.ai")
9
-
10
-
11
- def get_personal_repo_path() -> Optional[str]:
12
- """The personal repo (i.e. /repos/circlez/tal-circlez.ai), as PowerShell Get-PersonalRepoDirectory:
13
- PERSONAL_REPO_PATH when set, otherwise <REPOS_ORGANIZATION_BASE>/<username>-circlez.ai with the username from
14
- FULL_NAME, otherwise the only *-circlez.ai directory in REPOS_ORGANIZATION_BASE. None when it cannot be found"""
15
- personal_repo_path = os.environ.get("PERSONAL_REPO_PATH")
16
- if personal_repo_path:
17
- return personal_repo_path
18
- repos_organization_base = os.environ.get("REPOS_ORGANIZATION_BASE")
19
- if not repos_organization_base or not os.path.isdir(repos_organization_base):
20
- return None
21
- full_name = os.environ.get("FULL_NAME")
22
- username = _get_username_from_full_name(full_name) if full_name else None
23
- if username:
24
- personal_repo_path = os.path.join(repos_organization_base, username + PERSONAL_REPO_DIRECTORY_SUFFIX)
25
- if os.path.isdir(personal_repo_path):
26
- return personal_repo_path
27
- personal_repo_paths = [os.path.join(repos_organization_base, directory)
28
- for directory in os.listdir(repos_organization_base)
29
- if directory.endswith(PERSONAL_REPO_DIRECTORY_SUFFIX)
30
- and os.path.isdir(os.path.join(repos_organization_base, directory))]
31
- return personal_repo_paths[0] if len(personal_repo_paths) == 1 else None
@@ -1,11 +0,0 @@
1
- from typing import Optional
2
-
3
-
4
- def _get_username_from_full_name(full_name: str) -> Optional[str]:
5
- # Same as PowerShell User.psm1 Get-OurUsername
6
- if full_name == "Tal Rosenberger":
7
- return "tal"
8
- name_parts = full_name.split()
9
- if len(name_parts) != 2:
10
- return None
11
- return f"{name_parts[0]}.{name_parts[1][0]}".lower()