python-sdk-remote 0.0.188b3517__tar.gz → 0.0.190b3356__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.188b3517 → python_sdk_remote-0.0.190b3356}/PKG-INFO +1 -1
  2. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/__init__.py +2 -2
  3. python_sdk_remote-0.0.190b3356/python_sdk_remote/src/dot_env_files.py +80 -0
  4. python_sdk_remote-0.0.190b3356/python_sdk_remote/src/environment.py +21 -0
  5. python_sdk_remote-0.0.190b3356/python_sdk_remote/src/environment_variable_exception.py +2 -0
  6. python_sdk_remote-0.0.190b3356/python_sdk_remote/src/filesystem.py +15 -0
  7. python_sdk_remote-0.0.190b3356/python_sdk_remote/src/masking.py +8 -0
  8. python_sdk_remote-0.0.190b3356/python_sdk_remote/src/our_environment_variable.py +96 -0
  9. python_sdk_remote-0.0.188b3517/python_sdk_remote/src/environment_variable_exception.py → python_sdk_remote-0.0.190b3356/python_sdk_remote/src/our_environment_variable_exception.py +5 -1
  10. python_sdk_remote-0.0.190b3356/python_sdk_remote/src/personal_repo.py +31 -0
  11. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/unified_json.py +6 -2
  12. python_sdk_remote-0.0.190b3356/python_sdk_remote/src/username.py +11 -0
  13. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/utilities.py +28 -127
  14. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote.egg-info/PKG-INFO +1 -1
  15. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote.egg-info/SOURCES.txt +8 -0
  16. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/setup.py +1 -1
  17. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/README.md +0 -0
  18. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/pyproject.toml +0 -0
  19. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/constants.py +0 -0
  20. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/constants_src_mini_logger_and_logger.py +0 -0
  21. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/datetime_range.py +0 -0
  22. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/filename.py +0 -0
  23. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/generic_crud.py +0 -0
  24. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/http_response.py +0 -0
  25. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/is_test_data.py +0 -0
  26. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/item.py +0 -0
  27. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/mini_logger.py +0 -0
  28. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/our_obfuscation.py +0 -0
  29. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/our_object.py +0 -0
  30. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/our_objects.py +0 -0
  31. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/our_objects_local.py +0 -0
  32. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/our_objects_remote.py +0 -0
  33. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/our_request.py +0 -0
  34. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/our_request_old.py +0 -0
  35. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/valid_json_versions.py +0 -0
  36. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/validate_environment.py +0 -0
  37. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote/src/version.py +0 -0
  38. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote.egg-info/dependency_links.txt +0 -0
  39. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote.egg-info/requires.txt +0 -0
  40. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/python_sdk_remote.egg-info/top_level.txt +0 -0
  41. {python_sdk_remote-0.0.188b3517 → python_sdk_remote-0.0.190b3356}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-sdk-remote
3
- Version: 0.0.188b3517
3
+ Version: 0.0.190b3356
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
- EnvironmentVariableException,
13
+ OurEnvironmentVariableException,
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
- 'EnvironmentVariableException',
86
+ 'OurEnvironmentVariableException',
87
87
  'our_set_env',
88
88
  'our_delete_env',
89
89
  'to_dict',
@@ -0,0 +1,80 @@
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)
@@ -0,0 +1,21 @@
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
@@ -0,0 +1,2 @@
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
@@ -0,0 +1,15 @@
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
@@ -0,0 +1,8 @@
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:]
@@ -0,0 +1,96 @@
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,6 +1,10 @@
1
- class EnvironmentVariableException(Exception):
1
+ class OurEnvironmentVariableException(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
@@ -0,0 +1,31 @@
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
@@ -5,9 +5,8 @@ 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
9
8
  # 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)
10
- if json_version not in valid_json_versions:
9
+ if not self.is_valid_json(json_version):
11
10
  raise Exception(
12
11
  f"version {json_version} is not in valid_json_versions {valid_json_versions}, "
13
12
  f"please make sure you run sql2code."
@@ -15,6 +14,11 @@ class UnifiedJson:
15
14
  self.json_version = json_version
16
15
  self.data = data
17
16
 
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
+
18
22
  def get_unified_json(self):
19
23
  get_unified_json_result = {"version": self.json_version, "data": self.data}
20
24
  return get_unified_json_result
@@ -0,0 +1,11 @@
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()
@@ -17,7 +17,18 @@ from .version import PACKAGE_VERSION
17
17
 
18
18
  from .mini_logger import MiniLogger as logger
19
19
 
20
- from .environment_variable_exception import EnvironmentVariableException
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
21
32
 
22
33
  load_dotenv()
23
34
  # Load environment variables from .env file
@@ -127,31 +138,33 @@ def is_valid_time_range(time_range: tuple) -> bool:
127
138
 
128
139
 
129
140
  # Enhanced URL validation with support for both str and URL types
141
+
142
+
130
143
  def validate_url(url: Union[str, object]) -> bool:
131
144
  """
132
145
  Validate URL format.
133
-
146
+
134
147
  Args:
135
148
  url: URL string or URL object to validate
136
-
149
+
137
150
  Returns:
138
151
  bool: True if URL is valid, False otherwise
139
152
  """
140
153
  logger.start(object={"url": url, "url_type": type(url).__name__})
141
-
154
+
142
155
  # Handle URL object (if using yarl or similar library)
143
156
  if hasattr(url, 'scheme') and hasattr(url, 'host'):
144
157
  is_valid_url = bool(url.scheme and url.host)
145
158
  logger.end(object={"is_valid_url": is_valid_url, "source": "url_object"})
146
159
  return is_valid_url
147
-
160
+
148
161
  # Handle string URL
149
162
  if url is not None and url != "":
150
163
  parsed_url = urlparse(str(url))
151
164
  is_valid_url = bool(parsed_url.scheme and parsed_url.netloc)
152
165
  else:
153
166
  is_valid_url = True # Empty URL is considered valid (optional field)
154
-
167
+
155
168
  logger.end(object={"is_valid_url": is_valid_url, "source": "string_parsing"})
156
169
  return is_valid_url
157
170
 
@@ -306,110 +319,6 @@ def is_datetime_in_datetime_range(check_datetime: datetime, datetime_range: Tupl
306
319
  return is_datetime_in_datetime_range_result
307
320
 
308
321
 
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
-
413
322
  def get_brand_name(raise_if_not_found: bool = True,
414
323
  raise_if_empty: bool = False) -> Optional[str]:
415
324
  get_brand_name_result = our_get_env("BRAND_NAME",
@@ -418,19 +327,6 @@ def get_brand_name(raise_if_not_found: bool = True,
418
327
  return get_brand_name_result
419
328
 
420
329
 
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
-
434
330
  def get_product_user_identifier(raise_if_not_found: bool = True,
435
331
  raise_if_empty: bool = False) -> Optional[str]:
436
332
  get_product_user_identifier_result = our_get_env("PRODUCT_USER_IDENTIFIER",
@@ -443,7 +339,7 @@ def get_product_user_password(raise_if_not_found: bool = True,
443
339
  raise_if_empty: bool = False) -> Optional[str]:
444
340
  """
445
341
  Get product user password from environment variables.
446
-
342
+
447
343
  Note: PRODUCT_USER_PASSWORD is preferred over PRODUCT_PASSWORD (deprecated).
448
344
  This function maintains backward compatibility.
449
345
  """
@@ -461,6 +357,8 @@ def get_product_user_password(raise_if_not_found: bool = True,
461
357
 
462
358
 
463
359
  # Database-specific functions - Consider moving to database package when it's created
360
+
361
+
464
362
  def get_sql_hostname(raise_if_not_found: bool = True,
465
363
  raise_if_empty: bool = False) -> Optional[str]:
466
364
  get_sql_hostname_result = our_get_env("RDS_HOSTNAME",
@@ -481,6 +379,8 @@ def get_sql_password(raise_if_empty: bool = False) -> Optional[str]:
481
379
 
482
380
 
483
381
  # Dialog-specific function - Consider moving to dialog repo when it's created
382
+
383
+
484
384
  def get_dialog_jwt_secret_key() -> Optional[str]:
485
385
  get_dialog_jwt_secret_key_result = our_get_env("DIALOG_JWT_SECRET_KEY", raise_if_empty=True)
486
386
  return get_dialog_jwt_secret_key_result
@@ -507,13 +407,13 @@ def decode_jwt(token: str, key: str) -> dict:
507
407
  def obfuscate_log_dict(log_dict: dict) -> dict:
508
408
  """
509
409
  Obfuscate sensitive keys in log dictionaries for security.
510
-
410
+
511
411
  Obfuscates keys containing:
512
412
  - password, secret, token, jwt
513
413
  - email (with variations like e-mail, e_mail)
514
414
  - phone, name (first/last/nick/user etc.)
515
415
  - address, ssn
516
-
416
+
517
417
  In play1 environment, returns original dict for debugging purposes.
518
418
  """
519
419
  environment_name = get_environment_name()
@@ -533,6 +433,7 @@ def obfuscate_log_dict(log_dict: dict) -> dict:
533
433
 
534
434
  # Tests for the following functions are implemented in utilities_test.py
535
435
 
436
+
536
437
  def remove_digits(text: str) -> str:
537
438
  remove_digits_result = ''.join(i for i in text if not i.isdigit())
538
439
  return remove_digits_result
@@ -588,7 +489,7 @@ def validate_arguments(args: dict) -> None:
588
489
  def reformat_time_string(input_str: str) -> str:
589
490
  """
590
491
  Reformat time string from compact format to HH:MM:SS:MS format.
591
-
492
+
592
493
  Example:
593
494
  "1234" -> "12:34:00:00"
594
495
  "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.188b3517
3
+ Version: 0.0.190b3356
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,13 +10,19 @@ 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
13
15
  python_sdk_remote/src/environment_variable_exception.py
14
16
  python_sdk_remote/src/filename.py
17
+ python_sdk_remote/src/filesystem.py
15
18
  python_sdk_remote/src/generic_crud.py
16
19
  python_sdk_remote/src/http_response.py
17
20
  python_sdk_remote/src/is_test_data.py
18
21
  python_sdk_remote/src/item.py
22
+ python_sdk_remote/src/masking.py
19
23
  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
20
26
  python_sdk_remote/src/our_obfuscation.py
21
27
  python_sdk_remote/src/our_object.py
22
28
  python_sdk_remote/src/our_objects.py
@@ -24,7 +30,9 @@ python_sdk_remote/src/our_objects_local.py
24
30
  python_sdk_remote/src/our_objects_remote.py
25
31
  python_sdk_remote/src/our_request.py
26
32
  python_sdk_remote/src/our_request_old.py
33
+ python_sdk_remote/src/personal_repo.py
27
34
  python_sdk_remote/src/unified_json.py
35
+ python_sdk_remote/src/username.py
28
36
  python_sdk_remote/src/utilities.py
29
37
  python_sdk_remote/src/valid_json_versions.py
30
38
  python_sdk_remote/src/validate_environment.py
@@ -10,7 +10,7 @@ setuptools.setup(
10
10
  # Every feature branch needs its own free beta: PyPI never accepts a version twice, and
11
11
  # dev's literal is always one that has already been published. b3517 is this branch's
12
12
  # Jira id. GA stays a [pub] commit on dev.
13
- version='0.0.188b3517', # https://pypi.org/project/python-sdk-remote#history
13
+ version='0.0.190b3356', # 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",