Skip to content

Conversation

@tisnik
Copy link
Contributor

@tisnik tisnik commented Aug 28, 2025

Description

LCORE-605: remove assert from production code

Type of change

  • Refactor
  • New feature
  • Bug fix
  • CVE fix
  • Optimization
  • Documentation Update
  • Configuration Update
  • Bump-up service version
  • Bump-up dependent library
  • Bump-up library or tool used for development (does not change the final image)
  • CI configuration change
  • Konflux configuration change
  • Unit tests improvement
  • Integration tests improvement
  • End to end tests improvement

Related Tickets & Documents

  • Related Issue #LCORE-605

Summary by CodeRabbit

  • New Features

    • Standardized a clear configuration error type and consistent messages when app configuration isn't loaded.
  • Bug Fixes

    • Applied safe defaults to avoid crashes when authorization settings are missing.
    • Now raises a clear, explicit error when authentication settings are missing instead of failing ambiguously.
    • Ensured configuration validation is always enforced, yielding more predictable behavior across environments.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 28, 2025

Walkthrough

Adds a new public exception LogicError and replaces per-property assertions with inline guards that raise LogicError when configuration is not loaded. authentication_configuration now raises LogicError if authentication is missing; authorization_configuration returns a default when absent. AuthenticationConfiguration.jwk_configuration now raises ValueError. Tests updated to expect LogicError.

Changes

Cohort / File(s) Summary of changes
Configuration core
src/configuration.py
Added LogicError(Exception); removed per-property assertions and replaced them with inline guards that raise LogicError("logic error: configuration is not loaded") when the AppConfig is not loaded; authentication_configuration now raises LogicError if authentication is None; authorization_configuration returns AuthorizationConfiguration() when authorization is None. No new public helper method added.
Models — auth JWK validation
src/models/config.py
Replaced an assert self.jwk_config is not None with an explicit if self.jwk_config is None: raise ValueError("JWK configuration should not be None") in AuthenticationConfiguration.jwk_configuration.
Unit tests
tests/unit/test_configuration.py
Updated tests to import and expect LogicError instead of AssertionError when accessing configuration before it is loaded; error message strings unchanged.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller
  participant AppConfig
  note over AppConfig: Inline loaded-state guards (no new helper)
  Caller->>AppConfig: access property (e.g., configuration, inference)
  alt configuration not loaded
    AppConfig-->>Caller: raise LogicError("logic error: configuration is not loaded")
  else configuration loaded
    AppConfig-->>Caller: return requested property
  end
Loading
sequenceDiagram
  autonumber
  actor Caller
  participant AppConfig
  note over AppConfig: authorization returns default when absent
  Caller->>AppConfig: authorization_configuration
  AppConfig->>AppConfig: check inline authorization value
  alt authorization is None
    AppConfig-->>Caller: return AuthorizationConfiguration()
  else authorization present
    AppConfig-->>Caller: return configured authorization
  end
Loading
sequenceDiagram
  autonumber
  actor Caller
  participant AuthCfg as AuthenticationConfiguration
  note over AuthCfg: explicit JWK validation
  Caller->>AuthCfg: jwk_configuration()
  alt jwk_config is None
    AuthCfg-->>Caller: raise ValueError("JWK configuration should not be None")
  else jwk_config present
    AuthCfg-->>Caller: return jwk_config
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I nibble through configs, nose to the log,
A LogicError now guards every fog.
No fragile asserts left in the den,
Defaults for auth, and JWKs say when.
Hop—tests green—thump-thump, rabbit grin. 🐇

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@tisnik tisnik force-pushed the lcore-605-remove-assert-from-production-code branch from dfc7e52 to c4a8c83 Compare August 28, 2025 08:21
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/configuration.py (2)

47-54: Do not log full configuration; add error handling and redact secrets.

Logging the entire config at info level risks leaking API keys/tokens; also no handling for missing/invalid YAML.

Apply:

 def load_configuration(self, filename: str) -> None:
     """Load configuration from YAML file."""
-    with open(filename, encoding="utf-8") as fin:
-        config_dict = yaml.safe_load(fin)
-        config_dict = replace_env_vars(config_dict)
-        logger.info("Loaded configuration: %s", config_dict)
-        self.init_from_dict(config_dict)
+    try:
+        with open(filename, encoding="utf-8") as fin:
+            loaded = yaml.safe_load(fin)
+    except FileNotFoundError as exc:
+        raise FileNotFoundError(f"Configuration file not found: {filename}") from exc
+    except yaml.YAMLError as exc:
+        raise ValueError(f"Invalid YAML in configuration file: {filename}") from exc
+    if not isinstance(loaded, dict):
+        raise TypeError("Configuration root must be a mapping (YAML object).")
+    config_dict = replace_env_vars(loaded)
+    logger.debug("Loaded configuration keys: %s", list(config_dict.keys()))
+    self.init_from_dict(config_dict)

55-58: Validate input to prevent runtime type errors.

 def init_from_dict(self, config_dict: dict[Any, Any]) -> None:
     """Initialize configuration from a dictionary."""
-    self._configuration = Configuration(**config_dict)
+    if not isinstance(config_dict, dict):
+        raise TypeError("init_from_dict expects a dictionary mapping.")
+    self._configuration = Configuration(**config_dict)
🧹 Nitpick comments (3)
src/configuration.py (3)

62-63: DRY the loaded-state guard by delegating to configuration.

Leverage the configuration property’s guard to eliminate repetition.

 def service_configuration(self) -> ServiceConfiguration:
     """Return service configuration."""
-    if self._configuration is None:
-        raise LogicError("logic error: configuration is not loaded")
-    return self._configuration.service
+    return self.configuration.service

 def llama_stack_configuration(self) -> LlamaStackConfiguration:
     """Return Llama stack configuration."""
-    if self._configuration is None:
-        raise LogicError("logic error: configuration is not loaded")
-    return self._configuration.llama_stack
+    return self.configuration.llama_stack

 def user_data_collection_configuration(self) -> UserDataCollection:
     """Return user data collection configuration."""
-    if self._configuration is None:
-        raise LogicError("logic error: configuration is not loaded")
-    return self._configuration.user_data_collection
+    return self.configuration.user_data_collection

 def mcp_servers(self) -> list[ModelContextProtocolServer]:
     """Return model context protocol servers configuration."""
-    if self._configuration is None:
-        raise LogicError("logic error: configuration is not loaded")
-    return self._configuration.mcp_servers
+    return self.configuration.mcp_servers

 def authentication_configuration(self) -> AuthenticationConfiguration:
     """Return authentication configuration."""
-    if self._configuration is None:
-        raise LogicError("logic error: configuration is not loaded")
-
-    if self._configuration.authentication is None:
-        raise LogicError("logic error: authentication configuration is not loaded")
-
-    return self._configuration.authentication
+    _cfg = self.configuration
+    if _cfg.authentication is None:
+        raise LogicError("logic error: authentication configuration is not loaded")
+    return _cfg.authentication

 def authorization_configuration(self) -> AuthorizationConfiguration:
     """Return authorization configuration or default no-op configuration."""
-    if self._configuration is None:
-        raise LogicError("logic error: configuration is not loaded")
-
-    if self._configuration.authorization is None:
-        return AuthorizationConfiguration()
-
-    return self._configuration.authorization
+    _cfg = self.configuration
+    return _cfg.authorization or AuthorizationConfiguration()

 def customization(self) -> Optional[Customization]:
     """Return customization configuration."""
-    if self._configuration is None:
-        raise LogicError("logic error: configuration is not loaded")
-    return self._configuration.customization
+    return self.configuration.customization

 def inference(self) -> InferenceConfiguration:
     """Return inference configuration."""
-    if self._configuration is None:
-        raise LogicError("logic error: configuration is not loaded")
-    return self._configuration.inference
+    return self.configuration.inference

 def database_configuration(self) -> DatabaseConfiguration:
     """Return database configuration."""
-    if self._configuration is None:
-        raise LogicError("logic error: configuration is not loaded")
-    return self._configuration.database
+    return self.configuration.database

Also applies to: 69-71, 76-78, 83-85, 90-92, 97-99, 108-110, 119-121, 126-128, 133-135


108-114: Avoid returning a fresh default Authorization object on every access.

Callers mutating access_rules won’t see persistence between calls. Cache a singleton default or document immutability.

Within this property (if you choose to cache):

-    _cfg = self.configuration
-    return _cfg.authorization or AuthorizationConfiguration()
+    _cfg = self.configuration
+    return _cfg.authorization or self._default_authorization

And initialize once in __init__ (outside this range):

# in __init__
self._default_authorization = AuthorizationConfiguration()

28-30: Prefer deriving from RuntimeError; consider centralizing exceptions.

Minor: deriving from RuntimeError is semantically closer for program-logic faults. Optionally move to a shared errors module if reused.

-class LogicError(Exception):
+class LogicError(RuntimeError):
     """Error in application logic."""
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between dfc7e52 and c4a8c83.

📒 Files selected for processing (2)
  • src/configuration.py (3 hunks)
  • tests/unit/test_configuration.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/unit/test_configuration.py
🧰 Additional context used
🧬 Code graph analysis (1)
src/configuration.py (1)
src/models/config.py (8)
  • ServiceConfiguration (129-146)
  • LlamaStackConfiguration (157-200)
  • UserDataCollection (203-220)
  • ModelContextProtocolServer (149-154)
  • AuthenticationConfiguration (341-377)
  • AuthorizationConfiguration (316-321)
  • InferenceConfiguration (398-415)
  • DatabaseConfiguration (88-126)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-pr
🔇 Additional comments (1)
src/configuration.py (1)

100-102: LGTM: Raising LogicError for missing authentication.

Matches the PR goal to remove asserts and surface a domain exception.

Comment on lines +28 to +30
class LogicError(Exception):
"""Error in application logic."""

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify no remaining assert in production code (repo-wide).


🏁 Script executed:

#!/bin/bash
# Find asserts outside tests and docs.
rg -nP --type=python -g '!**/tests/**' -g '!**/doc/**' -C2 '^\s*assert\b'

Length of output: 106


Verify no remaining assert in production code (repo-wide).


🏁 Script executed:

#!/bin/bash
# Find 'assert' statements outside tests and docs.
rg -nP -g '*.py' -g '!**/tests/**' -g '!**/doc/**' -g '!**/docs/**' -C2 '^\s*assert\b'

Length of output: 1892


Remove all assert statements from production code

  • scripts/generate_openapi_schema.py (lines 31–35, 86–88): replace each assert with explicit validation and raise a specific exception (e.g. ValueError or a custom error).
  • src/app/database.py (lines 118, 122): replace type-check assert with runtime checks that raise TypeError or a domain-specific exception.

@tisnik tisnik merged commit e3019d5 into lightspeed-core:main Aug 28, 2025
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant