cycode 3.25.2.dev1__py3-none-any.whl → 3.25.2.dev2__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.
Files changed (123) hide show
  1. cycode/__init__.py +1 -1
  2. cycode/cli/app.py +9 -10
  3. cycode/cli/apps/activation_manager.py +6 -6
  4. cycode/cli/apps/ai_guardrails/command_utils.py +1 -2
  5. cycode/cli/apps/ai_guardrails/hooks_manager.py +6 -7
  6. cycode/cli/apps/ai_guardrails/ides/_plugin_utils.py +6 -5
  7. cycode/cli/apps/ai_guardrails/ides/_skill_utils.py +1 -2
  8. cycode/cli/apps/ai_guardrails/ides/base.py +9 -11
  9. cycode/cli/apps/ai_guardrails/ides/claude_code.py +14 -14
  10. cycode/cli/apps/ai_guardrails/ides/codex.py +11 -11
  11. cycode/cli/apps/ai_guardrails/ides/copilot.py +7 -7
  12. cycode/cli/apps/ai_guardrails/ides/cursor.py +5 -5
  13. cycode/cli/apps/ai_guardrails/install_command.py +3 -3
  14. cycode/cli/apps/ai_guardrails/scan/guardrail_config.py +10 -11
  15. cycode/cli/apps/ai_guardrails/scan/handlers.py +15 -14
  16. cycode/cli/apps/ai_guardrails/scan/mcp_server_status.py +6 -6
  17. cycode/cli/apps/ai_guardrails/scan/payload.py +13 -14
  18. cycode/cli/apps/ai_guardrails/scan/policy.py +3 -3
  19. cycode/cli/apps/ai_guardrails/scan/scan_command.py +4 -4
  20. cycode/cli/apps/ai_guardrails/session_start_command.py +8 -8
  21. cycode/cli/apps/ai_guardrails/status_command.py +2 -2
  22. cycode/cli/apps/ai_guardrails/uninstall_command.py +2 -2
  23. cycode/cli/apps/api/__init__.py +3 -3
  24. cycode/cli/apps/api/api_command.py +6 -6
  25. cycode/cli/apps/api/openapi_spec.py +4 -5
  26. cycode/cli/apps/auth/auth_common.py +4 -4
  27. cycode/cli/apps/configure/configure_command.py +2 -4
  28. cycode/cli/apps/configure/messages.py +2 -4
  29. cycode/cli/apps/configure/prompts.py +5 -7
  30. cycode/cli/apps/ignore/ignore_command.py +7 -7
  31. cycode/cli/apps/mcp/mcp_command.py +11 -11
  32. cycode/cli/apps/report/sbom/common.py +5 -5
  33. cycode/cli/apps/report/sbom/sbom_command.py +2 -2
  34. cycode/cli/apps/report/sbom/sbom_report_file.py +1 -2
  35. cycode/cli/apps/report_import/sbom/sbom_command.py +3 -3
  36. cycode/cli/apps/sca_options.py +3 -3
  37. cycode/cli/apps/scan/aggregation_report.py +4 -4
  38. cycode/cli/apps/scan/code_scanner.py +8 -7
  39. cycode/cli/apps/scan/commit_range_scanner.py +10 -10
  40. cycode/cli/apps/scan/detection_excluder.py +2 -4
  41. cycode/cli/apps/scan/pre_commit/pre_commit_command.py +3 -3
  42. cycode/cli/apps/scan/pre_push/pre_push_command.py +3 -3
  43. cycode/cli/apps/scan/pre_receive/pre_receive_command.py +2 -2
  44. cycode/cli/apps/scan/remote_url_resolver.py +6 -8
  45. cycode/cli/apps/scan/repository/repository_command.py +2 -2
  46. cycode/cli/apps/scan/scan_command.py +2 -2
  47. cycode/cli/apps/scan/scan_parameters.py +1 -3
  48. cycode/cli/apps/scan/scan_result.py +5 -5
  49. cycode/cli/console.py +2 -2
  50. cycode/cli/exceptions/custom_exceptions.py +1 -1
  51. cycode/cli/exceptions/handle_errors.py +1 -3
  52. cycode/cli/exceptions/handle_scan_errors.py +1 -2
  53. cycode/cli/files_collector/commit_range_documents.py +14 -14
  54. cycode/cli/files_collector/models/in_memory_zip.py +3 -3
  55. cycode/cli/files_collector/repository_documents.py +3 -3
  56. cycode/cli/files_collector/sca/base_restore_dependencies.py +7 -8
  57. cycode/cli/files_collector/sca/go/restore_go_dependencies.py +1 -2
  58. cycode/cli/files_collector/sca/maven/restore_gradle_dependencies.py +2 -3
  59. cycode/cli/files_collector/sca/maven/restore_maven_dependencies.py +3 -4
  60. cycode/cli/files_collector/sca/npm/restore_bun_dependencies.py +3 -4
  61. cycode/cli/files_collector/sca/npm/restore_deno_dependencies.py +1 -2
  62. cycode/cli/files_collector/sca/npm/restore_pnpm_dependencies.py +2 -3
  63. cycode/cli/files_collector/sca/npm/restore_yarn_dependencies.py +2 -3
  64. cycode/cli/files_collector/sca/npm/workspace/coverage.py +5 -5
  65. cycode/cli/files_collector/sca/npm/workspace/files.py +2 -3
  66. cycode/cli/files_collector/sca/npm/workspace/globs.py +1 -2
  67. cycode/cli/files_collector/sca/npm/workspace/resolvers.py +14 -16
  68. cycode/cli/files_collector/sca/php/restore_composer_dependencies.py +1 -2
  69. cycode/cli/files_collector/sca/python/restore_pip_dependencies.py +2 -3
  70. cycode/cli/files_collector/sca/python/restore_pipenv_dependencies.py +1 -2
  71. cycode/cli/files_collector/sca/python/restore_poetry_dependencies.py +2 -3
  72. cycode/cli/files_collector/sca/python/restore_uv_dependencies.py +2 -3
  73. cycode/cli/files_collector/sca/sca_file_collector.py +5 -5
  74. cycode/cli/files_collector/zip_documents.py +1 -2
  75. cycode/cli/models.py +8 -8
  76. cycode/cli/printers/console_printer.py +9 -9
  77. cycode/cli/printers/json_printer.py +2 -2
  78. cycode/cli/printers/printer_base.py +3 -3
  79. cycode/cli/printers/rich_printer.py +2 -2
  80. cycode/cli/printers/tables/table.py +4 -4
  81. cycode/cli/printers/tables/table_models.py +2 -2
  82. cycode/cli/printers/tables/table_printer_base.py +2 -2
  83. cycode/cli/printers/text_printer.py +4 -4
  84. cycode/cli/printers/utils/code_snippet_syntax.py +1 -1
  85. cycode/cli/printers/utils/detection_data.py +3 -3
  86. cycode/cli/printers/utils/sca_ossf.py +4 -4
  87. cycode/cli/printers/utils/sca_policy_details.py +2 -1
  88. cycode/cli/user_settings/config_file_manager.py +10 -10
  89. cycode/cli/user_settings/configuration_manager.py +6 -6
  90. cycode/cli/user_settings/credentials_manager.py +5 -6
  91. cycode/cli/utils/get_api_client.py +5 -5
  92. cycode/cli/utils/git_proxy.py +8 -8
  93. cycode/cli/utils/host_info.py +21 -38
  94. cycode/cli/utils/ignore_utils.py +13 -15
  95. cycode/cli/utils/jwt_utils.py +2 -4
  96. cycode/cli/utils/path_utils.py +6 -6
  97. cycode/cli/utils/progress_bar.py +4 -4
  98. cycode/cli/utils/scan_batch.py +2 -1
  99. cycode/cli/utils/scan_utils.py +2 -2
  100. cycode/cli/utils/shell_executor.py +3 -4
  101. cycode/cli/utils/task_timer.py +4 -4
  102. cycode/cli/utils/trust_store.py +0 -17
  103. cycode/cli/utils/url_utils.py +1 -2
  104. cycode/cli/utils/version_checker.py +4 -5
  105. cycode/cli/utils/yaml_utils.py +1 -1
  106. cycode/config.py +1 -2
  107. cycode/cyclient/ai_security_manager_client.py +17 -17
  108. cycode/cyclient/auth_client.py +2 -4
  109. cycode/cyclient/base_token_auth_client.py +2 -2
  110. cycode/cyclient/client_creator.py +4 -6
  111. cycode/cyclient/cycode_client_base.py +10 -9
  112. cycode/cyclient/cycode_dev_based_client.py +1 -3
  113. cycode/cyclient/headers.py +1 -2
  114. cycode/cyclient/import_sbom_client.py +3 -4
  115. cycode/cyclient/models.py +33 -33
  116. cycode/cyclient/report_client.py +1 -2
  117. cycode/cyclient/scan_client.py +8 -7
  118. cycode/logger.py +4 -4
  119. {cycode-3.25.2.dev1.dist-info → cycode-3.25.2.dev2.dist-info}/METADATA +12 -19
  120. {cycode-3.25.2.dev1.dist-info → cycode-3.25.2.dev2.dist-info}/RECORD +123 -123
  121. {cycode-3.25.2.dev1.dist-info → cycode-3.25.2.dev2.dist-info}/WHEEL +1 -1
  122. {cycode-3.25.2.dev1.dist-info → cycode-3.25.2.dev2.dist-info}/entry_points.txt +0 -0
  123. {cycode-3.25.2.dev1.dist-info → cycode-3.25.2.dev2.dist-info}/licenses/LICENCE +0 -0
@@ -1,11 +1,11 @@
1
1
  from _thread import interrupt_main
2
+ from collections.abc import Callable
2
3
  from threading import Event, Thread
3
4
  from types import TracebackType
4
- from typing import Callable, Optional
5
5
 
6
6
 
7
7
  class FunctionContext:
8
- def __init__(self, function: Callable, args: Optional[list] = None, kwargs: Optional[dict] = None) -> None:
8
+ def __init__(self, function: Callable, args: list | None = None, kwargs: dict | None = None) -> None:
9
9
  self.function = function
10
10
  self.args = args or []
11
11
  self.kwargs = kwargs or {}
@@ -54,7 +54,7 @@ class TimeoutAfter:
54
54
  the default option is to interrupt main thread
55
55
  """
56
56
 
57
- def __init__(self, timeout: int, quit_function: Optional[FunctionContext] = None) -> None:
57
+ def __init__(self, timeout: int, quit_function: FunctionContext | None = None) -> None:
58
58
  self.timeout = timeout
59
59
  self._quit_function = quit_function or FunctionContext(function=self.timeout_function)
60
60
  self.timer = TimerThread(timeout, quit_function=self._quit_function)
@@ -64,7 +64,7 @@ class TimeoutAfter:
64
64
  self.timer.start()
65
65
 
66
66
  def __exit__(
67
- self, exc_type: Optional[type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType]
67
+ self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
68
68
  ) -> None:
69
69
  if self.timeout:
70
70
  self.timer.stop()
@@ -1,21 +1,12 @@
1
- import sys
2
-
3
1
  from cycode import config
4
2
  from cycode.cli import consts
5
3
  from cycode.logger import get_logger
6
4
 
7
5
  logger = get_logger('Trust Store')
8
6
 
9
- # truststore requires Python 3.10+, so on 3.9 the OS trust store is unavailable
10
- _MIN_PYTHON_VERSION = (3, 10)
11
-
12
7
  _installed = False
13
8
 
14
9
 
15
- def is_supported() -> bool:
16
- return sys.version_info >= _MIN_PYTHON_VERSION
17
-
18
-
19
10
  def is_enabled() -> bool:
20
11
  return config.get_val_as_bool(consts.ENABLE_TRUSTSTORE_ENV_VAR_NAME)
21
12
 
@@ -45,14 +36,6 @@ def install() -> bool:
45
36
  )
46
37
  return False
47
38
 
48
- if not is_supported():
49
- logger.warning(
50
- 'OS trust store requires Python %s+, using the bundled CA store (certifi). Current version: %s',
51
- '.'.join(map(str, _MIN_PYTHON_VERSION)),
52
- '.'.join(map(str, sys.version_info[:3])),
53
- )
54
- return False
55
-
56
39
  try:
57
40
  import truststore
58
41
 
@@ -1,4 +1,3 @@
1
- from typing import Optional
2
1
  from urllib.parse import urlparse, urlunparse
3
2
 
4
3
  from cycode.logger import get_logger
@@ -6,7 +5,7 @@ from cycode.logger import get_logger
6
5
  logger = get_logger('URL Utils')
7
6
 
8
7
 
9
- def sanitize_repository_url(url: Optional[str]) -> Optional[str]:
8
+ def sanitize_repository_url(url: str | None) -> str | None:
10
9
  """Remove credentials (username, password, tokens) from repository URL.
11
10
 
12
11
  This function sanitizes repository URLs to prevent sending PAT tokens or other
@@ -2,7 +2,6 @@ import os
2
2
  import re
3
3
  import time
4
4
  from pathlib import Path
5
- from typing import Optional
6
5
 
7
6
  from cycode.cli.console import console
8
7
  from cycode.cli.user_settings.configuration_manager import ConfigurationManager
@@ -19,7 +18,7 @@ def _compare_versions(
19
18
  current_is_pre: bool,
20
19
  latest_is_pre: bool,
21
20
  latest_version: str,
22
- ) -> Optional[str]:
21
+ ) -> str | None:
23
22
  """Compare version numbers and determine if an update is needed.
24
23
 
25
24
  Implements version comparison logic with special handling for pre-release versions:
@@ -43,7 +42,7 @@ def _compare_versions(
43
42
  return None
44
43
 
45
44
  # Compare version numbers
46
- for current, latest in zip(current_parts, latest_parts):
45
+ for current, latest in zip(current_parts, latest_parts, strict=False):
47
46
  if latest > current:
48
47
  return latest_version
49
48
  if current > latest:
@@ -78,7 +77,7 @@ class VersionChecker(CycodeClientBase):
78
77
  config_dir = configuration_manager.global_config_file_manager.get_config_directory_path()
79
78
  self.cache_file = Path(config_dir) / '.version_check'
80
79
 
81
- def get_latest_version(self) -> Optional[str]:
80
+ def get_latest_version(self) -> str | None:
82
81
  """Fetch the latest version of the package from PyPI.
83
82
 
84
83
  Makes an HTTP request to PyPI's JSON API to get the latest version information.
@@ -160,7 +159,7 @@ class VersionChecker(CycodeClientBase):
160
159
  except Exception as e:
161
160
  logger.debug('Failed to update version check cache file: %s', {'file': self.cache_file}, exc_info=e)
162
161
 
163
- def check_for_update(self, current_version: str, use_cache: bool = True) -> Optional[str]:
162
+ def check_for_update(self, current_version: str, use_cache: bool = True) -> str | None:
164
163
  """Check if an update is available for the current version.
165
164
 
166
165
  Respects the update check frequency (daily/weekly) based on the version type
@@ -15,7 +15,7 @@ def _deep_update(source: dict[Hashable, Any], overrides: dict[Hashable, Any]) ->
15
15
  if isinstance(value, dict) and value:
16
16
  source[key] = _deep_update(source.get(key, {}), value)
17
17
  else:
18
- source[key] = overrides[key]
18
+ source[key] = value
19
19
 
20
20
  return source
21
21
 
cycode/config.py CHANGED
@@ -1,6 +1,5 @@
1
1
  import logging
2
2
  import os
3
- from typing import Optional
4
3
  from urllib.parse import urlparse
5
4
 
6
5
  from cycode.cli import consts
@@ -26,7 +25,7 @@ def get_val_as_bool(key: str, default: bool = False) -> bool:
26
25
  return configuration[key].lower() in {'true', '1', 'yes', 'y', 'on', 'enabled'}
27
26
 
28
27
 
29
- def get_val_as_int(key: str) -> Optional[int]:
28
+ def get_val_as_int(key: str) -> int | None:
30
29
  val = configuration.get(key)
31
30
  if not val:
32
31
  return None
@@ -1,6 +1,6 @@
1
1
  """Client for AI Security Manager service."""
2
2
 
3
- from typing import TYPE_CHECKING, Optional
3
+ from typing import TYPE_CHECKING
4
4
 
5
5
  from cycode.cli.exceptions.custom_exceptions import HttpUnauthorizedError
6
6
  from cycode.cyclient.cycode_client_base import CycodeClientBase
@@ -32,7 +32,7 @@ class AISecurityManagerClient:
32
32
  return f'{service_name}/{path}'
33
33
  return path
34
34
 
35
- def create_conversation(self, payload: 'AIHookPayload') -> Optional[str]:
35
+ def create_conversation(self, payload: 'AIHookPayload') -> str | None:
36
36
  """Creates an AI conversation from hook payload."""
37
37
  conversation_id = payload.conversation_id
38
38
  if not conversation_id:
@@ -63,10 +63,10 @@ class AISecurityManagerClient:
63
63
  payload: 'AIHookPayload',
64
64
  event_type: 'AiHookEventType',
65
65
  outcome: 'AIHookOutcome',
66
- scan_id: Optional[str] = None,
67
- block_reason: Optional['BlockReason'] = None,
68
- error_message: Optional[str] = None,
69
- file_path: Optional[str] = None,
66
+ scan_id: str | None = None,
67
+ block_reason: 'BlockReason | None' = None,
68
+ error_message: str | None = None,
69
+ file_path: str | None = None,
70
70
  ) -> None:
71
71
  """Create an AI hook event from hook payload."""
72
72
  conversation_id = payload.conversation_id
@@ -95,7 +95,7 @@ class AISecurityManagerClient:
95
95
  logger.debug('Failed to create AI hook event', exc_info=e)
96
96
  # Don't fail the hook if tracking fails
97
97
 
98
- def get_resolved_guardrails(self) -> Optional[dict]:
98
+ def get_resolved_guardrails(self) -> dict | None:
99
99
  """Fetch the tenant's resolved guardrail config (per-agent modes + sensitive-path globs)."""
100
100
  try:
101
101
  response = self.client.get(self._build_endpoint_path(self._RESOLVED_GUARDRAILS_PATH))
@@ -106,16 +106,16 @@ class AISecurityManagerClient:
106
106
 
107
107
  def report_session_context(
108
108
  self,
109
- hostname: Optional[str] = None,
110
- platform_name: Optional[str] = None,
111
- os_version: Optional[str] = None,
112
- serial_number: Optional[str] = None,
113
- last_login_user: Optional[str] = None,
114
- config_files: Optional[list[dict]] = None,
115
- enabled_plugins: Optional[dict] = None,
116
- skill_files: Optional[list[dict]] = None,
117
- user_email: Optional[str] = None,
118
- ) -> Optional[SessionContextResponse]:
109
+ hostname: str | None = None,
110
+ platform_name: str | None = None,
111
+ os_version: str | None = None,
112
+ serial_number: str | None = None,
113
+ last_login_user: str | None = None,
114
+ config_files: list[dict] | None = None,
115
+ enabled_plugins: dict | None = None,
116
+ skill_files: list[dict] | None = None,
117
+ user_email: str | None = None,
118
+ ) -> SessionContextResponse | None:
119
119
  """Report session context to the backend. Returns None when the report was not accepted."""
120
120
  body: dict = {
121
121
  'hostname': hostname,
@@ -1,5 +1,3 @@
1
- from typing import Optional
2
-
3
1
  from requests import Request, Response
4
2
 
5
3
  from cycode.cli.exceptions.custom_exceptions import HttpUnauthorizedError, RequestHttpError
@@ -24,7 +22,7 @@ class AuthClient:
24
22
  response = self.cycode_client.post(url_path=path, body=body)
25
23
  return self.parse_start_session_response(response)
26
24
 
27
- def get_api_token(self, session_id: str, code_verifier: str) -> Optional[models.ApiTokenGenerationPollingResponse]:
25
+ def get_api_token(self, session_id: str, code_verifier: str) -> models.ApiTokenGenerationPollingResponse | None:
28
26
  path = f'{self.AUTH_CONTROLLER_PATH}/token'
29
27
  body = {'session_id': session_id, 'code_verifier': code_verifier}
30
28
  try:
@@ -40,7 +38,7 @@ class AuthClient:
40
38
  return models.AuthenticationSessionSchema().load(response.json())
41
39
 
42
40
  @staticmethod
43
- def parse_api_token_polling_response(response: Response) -> Optional[models.ApiTokenGenerationPollingResponse]:
41
+ def parse_api_token_polling_response(response: Response) -> models.ApiTokenGenerationPollingResponse | None:
44
42
  try:
45
43
  return models.ApiTokenGenerationPollingResponseSchema().load(response.json())
46
44
  except Exception:
@@ -2,7 +2,7 @@ import secrets
2
2
  import time
3
3
  from abc import ABC, abstractmethod
4
4
  from threading import Lock
5
- from typing import Any, Optional
5
+ from typing import Any
6
6
 
7
7
  import arrow
8
8
  from requests import Response
@@ -97,7 +97,7 @@ class BaseTokenAuthClient(CycodeClient, ABC):
97
97
  jwt_creator = self._create_jwt_creator()
98
98
  self._credentials_manager.update_access_token(self._access_token, self._expires_in.timestamp(), jwt_creator)
99
99
 
100
- def get_request_headers(self, additional_headers: Optional[dict] = None, without_auth: bool = False) -> dict:
100
+ def get_request_headers(self, additional_headers: dict | None = None, without_auth: bool = False) -> dict:
101
101
  headers = super().get_request_headers(additional_headers=additional_headers)
102
102
 
103
103
  if not without_auth:
@@ -1,5 +1,3 @@
1
- from typing import Optional
2
-
3
1
  from cycode.cyclient.ai_security_manager_client import AISecurityManagerClient
4
2
  from cycode.cyclient.ai_security_manager_service_config import (
5
3
  DefaultAISecurityManagerServiceConfig,
@@ -17,7 +15,7 @@ from cycode.cyclient.scan_config_base import DefaultScanConfig, DevScanConfig
17
15
 
18
16
 
19
17
  def create_scan_client(
20
- client_id: str, client_secret: Optional[str] = None, hide_response_log: bool = False, id_token: Optional[str] = None
18
+ client_id: str, client_secret: str | None = None, hide_response_log: bool = False, id_token: str | None = None
21
19
  ) -> ScanClient:
22
20
  if dev_mode:
23
21
  client = CycodeDevBasedClient(DEV_CYCODE_API_URL)
@@ -33,7 +31,7 @@ def create_scan_client(
33
31
 
34
32
 
35
33
  def create_report_client(
36
- client_id: str, client_secret: Optional[str] = None, _: bool = False, id_token: Optional[str] = None
34
+ client_id: str, client_secret: str | None = None, _: bool = False, id_token: str | None = None
37
35
  ) -> ReportClient:
38
36
  if dev_mode:
39
37
  client = CycodeDevBasedClient(DEV_CYCODE_API_URL)
@@ -45,7 +43,7 @@ def create_report_client(
45
43
 
46
44
 
47
45
  def create_import_sbom_client(
48
- client_id: str, client_secret: Optional[str] = None, _: bool = False, id_token: Optional[str] = None
46
+ client_id: str, client_secret: str | None = None, _: bool = False, id_token: str | None = None
49
47
  ) -> ImportSbomClient:
50
48
  if dev_mode:
51
49
  client = CycodeDevBasedClient(DEV_CYCODE_API_URL)
@@ -57,7 +55,7 @@ def create_import_sbom_client(
57
55
 
58
56
 
59
57
  def create_ai_security_manager_client(
60
- client_id: str, client_secret: Optional[str] = None, _: bool = False, id_token: Optional[str] = None
58
+ client_id: str, client_secret: str | None = None, _: bool = False, id_token: str | None = None
61
59
  ) -> AISecurityManagerClient:
62
60
  if dev_mode:
63
61
  client = CycodeDevBasedClient(DEV_CYCODE_API_URL)
@@ -2,8 +2,9 @@ import functools
2
2
  import os
3
3
  import platform
4
4
  import ssl
5
+ from collections.abc import Callable
5
6
  from io import BytesIO
6
- from typing import TYPE_CHECKING, Callable, ClassVar, Optional
7
+ from typing import TYPE_CHECKING, ClassVar
7
8
 
8
9
  import requests
9
10
  from requests import Response, exceptions
@@ -114,7 +115,7 @@ def _should_retry_exception(exception: BaseException) -> bool:
114
115
  class UploadProgressTracker:
115
116
  """File-like wrapper that tracks bytes read during upload and fires a progress callback."""
116
117
 
117
- def __init__(self, data: bytes, callback: Optional[Callable[[int, int], None]]) -> None:
118
+ def __init__(self, data: bytes, callback: Callable[[int, int], None] | None) -> None:
118
119
  self._io = BytesIO(data)
119
120
  self._callback = callback
120
121
  self.bytes_read = 0
@@ -146,13 +147,13 @@ class CycodeClientBase:
146
147
  def enrich_user_agent(user_agent_suffix: str) -> None:
147
148
  CycodeClientBase.MANDATORY_HEADERS['User-Agent'] += f' {user_agent_suffix}'
148
149
 
149
- def post(self, url_path: str, body: Optional[dict] = None, headers: Optional[dict] = None, **kwargs) -> Response:
150
+ def post(self, url_path: str, body: dict | None = None, headers: dict | None = None, **kwargs) -> Response:
150
151
  return self._execute(method='post', endpoint=url_path, json=body, headers=headers, **kwargs)
151
152
 
152
- def put(self, url_path: str, body: Optional[dict] = None, headers: Optional[dict] = None, **kwargs) -> Response:
153
+ def put(self, url_path: str, body: dict | None = None, headers: dict | None = None, **kwargs) -> Response:
153
154
  return self._execute(method='put', endpoint=url_path, json=body, headers=headers, **kwargs)
154
155
 
155
- def get(self, url_path: str, headers: Optional[dict] = None, **kwargs) -> Response:
156
+ def get(self, url_path: str, headers: dict | None = None, **kwargs) -> Response:
156
157
  return self._execute(method='get', endpoint=url_path, headers=headers, **kwargs)
157
158
 
158
159
  def post_multipart(
@@ -160,7 +161,7 @@ class CycodeClientBase:
160
161
  url_path: str,
161
162
  form_fields: dict,
162
163
  files: dict,
163
- on_upload_progress: Optional[Callable[[int, int], None]] = None,
164
+ on_upload_progress: Callable[[int, int], None] | None = None,
164
165
  hide_response_content_log: bool = False,
165
166
  ) -> Response:
166
167
  """POST a multipart form body with optional upload progress tracking and retry."""
@@ -191,7 +192,7 @@ class CycodeClientBase:
191
192
  url: str,
192
193
  body: bytes,
193
194
  content_type: str,
194
- on_upload_progress: Optional[Callable[[int, int], None]],
195
+ on_upload_progress: Callable[[int, int], None] | None,
195
196
  hide_response_content_log: bool,
196
197
  ) -> Response:
197
198
  # Wrap the body in a fresh tracker each attempt so bytes_read starts from zero.
@@ -227,7 +228,7 @@ class CycodeClientBase:
227
228
  self,
228
229
  method: str,
229
230
  endpoint: str,
230
- headers: Optional[dict] = None,
231
+ headers: dict | None = None,
231
232
  without_auth: bool = False,
232
233
  hide_response_content_log: bool = False,
233
234
  **kwargs,
@@ -253,7 +254,7 @@ class CycodeClientBase:
253
254
  except Exception as e:
254
255
  self._handle_exception(e)
255
256
 
256
- def get_request_headers(self, additional_headers: Optional[dict] = None, **kwargs) -> dict[str, str]:
257
+ def get_request_headers(self, additional_headers: dict | None = None, **kwargs) -> dict[str, str]:
257
258
  if additional_headers is None:
258
259
  return self.MANDATORY_HEADERS.copy()
259
260
  return {**self.MANDATORY_HEADERS, **additional_headers}
@@ -1,5 +1,3 @@
1
- from typing import Optional
2
-
3
1
  from cycode.cyclient.config import dev_tenant_id
4
2
  from cycode.cyclient.cycode_client_base import CycodeClientBase
5
3
 
@@ -12,7 +10,7 @@ class CycodeDevBasedClient(CycodeClientBase):
12
10
  def __init__(self, api_url: str) -> None:
13
11
  super().__init__(api_url)
14
12
 
15
- def get_request_headers(self, additional_headers: Optional[dict] = None, **_) -> dict[str, str]:
13
+ def get_request_headers(self, additional_headers: dict | None = None, **_) -> dict[str, str]:
16
14
  headers = super().get_request_headers(additional_headers=additional_headers)
17
15
  headers['X-Tenant-Id'] = dev_tenant_id
18
16
 
@@ -1,5 +1,4 @@
1
1
  import platform
2
- from typing import Optional
3
2
  from uuid import uuid4
4
3
 
5
4
  from cycode import __version__
@@ -25,7 +24,7 @@ def get_cli_user_agent() -> str:
25
24
 
26
25
 
27
26
  class _CorrelationId:
28
- _id: Optional[str] = None
27
+ _id: str | None = None
29
28
 
30
29
  def get_correlation_id(self) -> str:
31
30
  """Get correlation ID.
@@ -1,6 +1,5 @@
1
1
  import dataclasses
2
2
  from pathlib import Path
3
- from typing import Optional
4
3
 
5
4
  from requests import Response
6
5
 
@@ -15,8 +14,8 @@ class ImportSbomParameters:
15
14
  Name: str
16
15
  Vendor: str
17
16
  BusinessImpact: BusinessImpactOption
18
- Labels: Optional[list[str]]
19
- Owners: Optional[list[str]]
17
+ Labels: list[str] | None
18
+ Owners: list[str] | None
20
19
 
21
20
  def _owners_to_ids(self) -> list[str]:
22
21
  return []
@@ -71,7 +70,7 @@ class ImportSbomClient:
71
70
  member_details = self.parse_requested_member_details_response(response)
72
71
 
73
72
  if not member_details.items:
74
- raise Exception(
73
+ raise Exception( # noqa: TRY002
75
74
  f"Failed to find user with email '{email}'. Verify this email is registered to Cycode platform"
76
75
  )
77
76
  return member_details.items.pop(0).external_id
cycode/cyclient/models.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from dataclasses import dataclass
2
2
  from enum import Enum
3
- from typing import Any, Optional
3
+ from typing import Any
4
4
 
5
5
  from marshmallow import EXCLUDE, Schema, fields, missing, post_load
6
6
 
@@ -13,8 +13,8 @@ class Detection(Schema):
13
13
  message: str,
14
14
  detection_details: dict,
15
15
  detection_rule_id: str,
16
- severity: Optional[str] = None,
17
- id: Optional[str] = None,
16
+ severity: str | None = None,
17
+ id: str | None = None,
18
18
  ) -> None:
19
19
  super().__init__()
20
20
  self.id = id
@@ -62,7 +62,7 @@ class DetectionSchema(Schema):
62
62
 
63
63
 
64
64
  class DetectionsPerFile(Schema):
65
- def __init__(self, file_name: str, detections: list[Detection], commit_id: Optional[str] = None) -> None:
65
+ def __init__(self, file_name: str, detections: list[Detection], commit_id: str | None = None) -> None:
66
66
  super().__init__()
67
67
  self.file_name = file_name
68
68
  self.detections = detections
@@ -74,10 +74,10 @@ class ZippedFileScanResult(Schema):
74
74
  self,
75
75
  did_detect: bool,
76
76
  detections_per_file: list[DetectionsPerFile],
77
- report_url: Optional[str] = None,
78
- scan_id: Optional[str] = None,
79
- err: Optional[str] = None,
80
- verdict: Optional[str] = None,
77
+ report_url: str | None = None,
78
+ scan_id: str | None = None,
79
+ err: str | None = None,
80
+ verdict: str | None = None,
81
81
  ) -> None:
82
82
  super().__init__()
83
83
  self.did_detect = did_detect
@@ -92,9 +92,9 @@ class ScanResult(Schema):
92
92
  def __init__(
93
93
  self,
94
94
  did_detect: bool,
95
- scan_id: Optional[str] = None,
96
- detections: Optional[list[Detection]] = None,
97
- err: Optional[str] = None,
95
+ scan_id: str | None = None,
96
+ detections: list[Detection] | None = None,
97
+ err: str | None = None,
98
98
  ) -> None:
99
99
  super().__init__()
100
100
  self.did_detect = did_detect
@@ -138,7 +138,7 @@ class UploadLinkResponseSchema(Schema):
138
138
 
139
139
 
140
140
  class ScanInitializationResponse(Schema):
141
- def __init__(self, scan_id: Optional[str] = None, err: Optional[str] = None) -> None:
141
+ def __init__(self, scan_id: str | None = None, err: str | None = None) -> None:
142
142
  super().__init__()
143
143
  self.scan_id = scan_id
144
144
  self.err = err
@@ -159,13 +159,13 @@ class ScanInitializationResponseSchema(Schema):
159
159
  class ScanDetailsResponse(Schema):
160
160
  def __init__(
161
161
  self,
162
- id: Optional[str] = None,
163
- scan_status: Optional[str] = None,
164
- results_count: Optional[int] = None,
165
- metadata: Optional[str] = None,
166
- message: Optional[str] = None,
167
- scan_update_at: Optional[str] = None,
168
- err: Optional[str] = None,
162
+ id: str | None = None,
163
+ scan_status: str | None = None,
164
+ results_count: int | None = None,
165
+ metadata: str | None = None,
166
+ message: str | None = None,
167
+ scan_update_at: str | None = None,
168
+ err: str | None = None,
169
169
  ) -> None:
170
170
  super().__init__()
171
171
  self.id = id
@@ -389,9 +389,9 @@ class SbomReportStorageDetailsSchema(Schema):
389
389
  class ReportExecution:
390
390
  id: int
391
391
  status: str
392
- error_message: Optional[str] = None
393
- status_message: Optional[str] = None
394
- storage_details: Optional[SbomReportStorageDetails] = None
392
+ error_message: str | None = None
393
+ status_message: str | None = None
394
+ storage_details: SbomReportStorageDetails | None = None
395
395
 
396
396
 
397
397
  class ReportExecutionSchema(Schema):
@@ -445,7 +445,7 @@ class MemberSchema(Schema):
445
445
  class MemberDetails:
446
446
  items: list[Member]
447
447
  page_size: int
448
- next_page_token: Optional[str]
448
+ next_page_token: str | None
449
449
 
450
450
 
451
451
  class RequestedMemberDetailsResultSchema(Schema):
@@ -481,11 +481,11 @@ class ClassificationDataSchema(Schema):
481
481
  class DetectionRule:
482
482
  classification_data: list[ClassificationData]
483
483
  detection_rule_id: str
484
- custom_remediation_guidelines: Optional[str] = None
485
- remediation_guidelines: Optional[str] = None
486
- description: Optional[str] = None
487
- policy_name: Optional[str] = None
488
- display_name: Optional[str] = None
484
+ custom_remediation_guidelines: str | None = None
485
+ remediation_guidelines: str | None = None
486
+ description: str | None = None
487
+ policy_name: str | None = None
488
+ display_name: str | None = None
489
489
 
490
490
 
491
491
  class DetectionRuleSchema(Schema):
@@ -509,7 +509,7 @@ class DetectionRuleSchema(Schema):
509
509
  class ScanResultsSyncFlow:
510
510
  id: str
511
511
  detection_messages: list[dict]
512
- verdict: Optional[str] = None
512
+ verdict: str | None = None
513
513
 
514
514
 
515
515
  class ScanResultsSyncFlowSchema(Schema):
@@ -594,7 +594,7 @@ class McpServerAuthorizationStatus(str, Enum):
594
594
 
595
595
 
596
596
  class McpServerAuthorizationStatusField(fields.Field):
597
- def _serialize(self, value: Optional[McpServerAuthorizationStatus], *_, **__) -> Optional[str]:
597
+ def _serialize(self, value: McpServerAuthorizationStatus | None, *_, **__) -> str | None:
598
598
  return value.value if value is not None else None
599
599
 
600
600
  def deserialize(self, value: object, *_, **__) -> McpServerAuthorizationStatus:
@@ -603,8 +603,8 @@ class McpServerAuthorizationStatusField(fields.Field):
603
603
 
604
604
  @dataclass
605
605
  class McpServerStatus:
606
- alias: Optional[str] = None
607
- normalized_id: Optional[str] = None
606
+ alias: str | None = None
607
+ normalized_id: str | None = None
608
608
  status: McpServerAuthorizationStatus = McpServerAuthorizationStatus.UNREVIEWED
609
609
 
610
610
 
@@ -624,7 +624,7 @@ class McpServerStatusSchema(Schema):
624
624
  @dataclass
625
625
  class SessionContextResponse:
626
626
  # None when the response carries no (or malformed) MCP server statuses.
627
- mcp_servers: Optional[list[McpServerStatus]] = None
627
+ mcp_servers: list[McpServerStatus] | None = None
628
628
 
629
629
 
630
630
  class SessionContextResponseSchema(Schema):
@@ -1,6 +1,5 @@
1
1
  import dataclasses
2
2
  import json
3
- from typing import Optional
4
3
 
5
4
  from requests import Response
6
5
 
@@ -45,7 +44,7 @@ class ReportClient:
45
44
  self.client = client
46
45
 
47
46
  def request_sbom_report_execution(
48
- self, params: ReportParameters, zip_file: InMemoryZip = None, repository_url: Optional[str] = None
47
+ self, params: ReportParameters, zip_file: InMemoryZip = None, repository_url: str | None = None
49
48
  ) -> models.ReportExecution:
50
49
  report_type = 'zipped-file' if zip_file else 'repository-url'
51
50
  url_path = f'{self.SERVICE_NAME}/{self.CREATE_SBOM_REPORT_REQUEST_PATH}'.format(report_type=report_type)
@@ -1,6 +1,7 @@
1
1
  import json
2
+ from collections.abc import Callable
2
3
  from copy import deepcopy
3
- from typing import TYPE_CHECKING, Callable, Optional, Union
4
+ from typing import TYPE_CHECKING
4
5
  from uuid import UUID
5
6
 
6
7
  import requests
@@ -119,7 +120,7 @@ class ScanClient:
119
120
  scan_parameters: dict,
120
121
  is_git_diff: bool = False,
121
122
  is_commit_range: bool = False,
122
- on_upload_progress: Optional[Callable[[int, int], None]] = None,
123
+ on_upload_progress: Callable[[int, int], None] | None = None,
123
124
  ) -> models.ScanInitializationResponse:
124
125
  response = self.scan_cycode_client.post_multipart(
125
126
  url_path=self.get_zipped_file_scan_async_url_path(scan_type),
@@ -145,7 +146,7 @@ class ScanClient:
145
146
  url: str,
146
147
  fields: dict[str, str],
147
148
  zip_file: 'InMemoryZip',
148
- on_upload_progress: Optional[Callable[[int, int], None]] = None,
149
+ on_upload_progress: Callable[[int, int], None] | None = None,
149
150
  ) -> None:
150
151
  all_files = {key: (None, value) for key, value in fields.items()}
151
152
  all_files['file'] = ('multiple_files_scan.zip', zip_file.read(), 'application/octet-stream')
@@ -315,7 +316,7 @@ class ScanClient:
315
316
  def parse_detection_rules_response(response: Response) -> list[models.DetectionRule]:
316
317
  return models.DetectionRuleSchema().load(response.json(), many=True)
317
318
 
318
- def get_detection_rules(self, detection_rules_ids: Union[set[str], list[str]]) -> list[models.DetectionRule]:
319
+ def get_detection_rules(self, detection_rules_ids: set[str] | list[str]) -> list[models.DetectionRule]:
319
320
  response = self.scan_cycode_client.get(
320
321
  url_path=self.get_detection_rules_path(),
321
322
  params={'ids': detection_rules_ids},
@@ -372,7 +373,7 @@ class ScanClient:
372
373
  correct_scan_type = self.scan_config.get_async_scan_type(scan_type)
373
374
  return f'{self.get_scan_service_url_path(scan_type)}/{correct_scan_type}/configuration'
374
375
 
375
- def get_scan_configuration(self, scan_type: str, remote_url: Optional[str] = None) -> models.ScanConfiguration:
376
+ def get_scan_configuration(self, scan_type: str, remote_url: str | None = None) -> models.ScanConfiguration:
376
377
  params = {}
377
378
  if remote_url:
378
379
  params['remote_url'] = remote_url
@@ -385,8 +386,8 @@ class ScanClient:
385
386
  return models.ScanConfigurationSchema().load(response.json())
386
387
 
387
388
  def get_scan_configuration_safe(
388
- self, scan_type: str, remote_url: Optional[str] = None
389
- ) -> Optional['models.ScanConfiguration']:
389
+ self, scan_type: str, remote_url: str | None = None
390
+ ) -> 'models.ScanConfiguration | None':
390
391
  try:
391
392
  return self.get_scan_configuration(scan_type, remote_url)
392
393
  except RequestHttpError as e: