Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance iRail class documentation and refactor code for clarity #45

Merged
merged 6 commits into from
Jan 28, 2025

Conversation

tjorim
Copy link
Owner

@tjorim tjorim commented Jan 28, 2025

Improve documentation for rate limiting methods, standardize type hints, introduce an OccupancyName enum, and simplify data class structures for better deserialization. Fix tests to ensure reliability.

Summary by CodeRabbit

  • Refactor

    • Updated type hints from Dict to dict across multiple methods and attributes in the iRail library.
    • Restructured data models in the library to simplify type definitions and improve type safety.
    • Simplified response structures and list handling in data models.
  • Tests

    • Enhanced readability and maintainability of unit tests with streamlined assertions and response structure adjustments.

Copy link
Contributor

coderabbitai bot commented Jan 28, 2025

📝 Walkthrough

Walkthrough

This pull request introduces modifications to the PyRail library's type hints and data model structure. The changes primarily focus on updating type hints from Dict to dict in the irail.py file, enhancing type safety. Additionally, the models.py file has been restructured to simplify data models, introduce new enumerations, and improve deserialization methods. The unit tests in test_irail.py have also been updated to reflect changes in data structure and improve code readability while maintaining the original testing logic.

Changes

File Changes
pyrail/irail.py - Replaced Dict type hints with dict for class attributes and method signatures
- Updated type hints for endpoints, etag_cache, and method parameters
- Slight rephrasing of docstring for _refill_tokens method
pyrail/models.py - Added new OccupancyName enum
- Updated Orientation enum
- Modified multiple classes to use direct list types instead of wrapper classes
- Added __pre_deserialize__ methods for flattening nested structures
- Updated type hints for various data models
tests/test_irail.py - Adjusted response structure access in tests
- Simplified assertions
- Updated list length checks
- Improved test code formatting

Sequence Diagram

sequenceDiagram
    participant Client
    participant iRail
    participant API
    
    Client->>iRail: Request data (e.g., liveboard)
    iRail->>API: Send async request
    API-->>iRail: Return response
    iRail->>iRail: Handle response
    iRail->>iRail: Deserialize data
    iRail-->>Client: Return typed response
Loading

Possibly related PRs

Poem

🚂 Hop along the type-safe track,
Dictionaries now sleek and compact!
Enums dancing, models refined,
Code complexity left behind.
PyRail's rabbit leaps with glee! 🐰✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e5d5d7 and bd40c63.

📒 Files selected for processing (2)
  • pyrail/models.py (14 hunks)
  • tests/test_irail.py (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_irail.py
🔇 Additional comments (7)
pyrail/models.py (7)

29-43: Well-structured enums with clear documentation!

The OccupancyName and Orientation enums are well-defined with descriptive docstrings and consistent naming conventions.


145-168: LGTM! Clean implementation of pre-deserialization logic.

The pre-deserialization method correctly handles null cases and flattens the nested structure. The type hints are properly updated to use list[T] instead of wrapper classes.


228-234: Clean implementation of stops pre-deserialization.

The pre-deserialization method correctly flattens the nested structure, following the iRail API's consistent pattern.


306-323: Well-implemented pre-deserialization logic.

The pre-deserialization method correctly handles the flattening of remarks, alerts, and vias, following the iRail API's consistent structure.


380-386: Clean implementation of stops pre-deserialization.

The pre-deserialization method correctly flattens the nested structure, following the iRail API's consistent pattern.


458-464: Clean implementation of units pre-deserialization.

The pre-deserialization method correctly flattens the nested structure, following the iRail API's consistent pattern.


512-520: Clean implementation of description links pre-deserialization.

The pre-deserialization method correctly flattens the nested structure, following the iRail API's consistent pattern.

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai 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:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @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

Documentation and Community

  • 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.

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: 4

🧹 Nitpick comments (5)
pyrail/models.py (5)

228-234: Potential KeyError risk on 'stops'.
If the JSON payload is missing the 'stops' or 'stop' keys, this will raise KeyError. Consider a safer approach, for example:

-    d["stops"] = d["stops"]["stop"]
+    if "stops" in d and d["stops"] and "stop" in d["stops"]:
+        d["stops"] = d["stops"]["stop"]
+    else:
+        d["stops"] = []

379-385: Handling potential missing 'stops'.
This similarly needs a KeyError check, as done for earlier classes.


457-463: KeyError fallback recommended.
When flattening 'units', consider a fallback if 'units' or 'unit' is absent.


480-486: Safer flattening recommended for 'segments'.
A KeyError can occur if "segments" or "segment" are missing. Consider a similar fallback approach.


511-519: Expand your error handling for 'descriptionLinks'.
Indexing directly can cause a KeyError if 'descriptionLinks' or 'descriptionLink' is absent.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c87930 and 5e5d5d7.

📒 Files selected for processing (3)
  • pyrail/irail.py (15 hunks)
  • pyrail/models.py (14 hunks)
  • tests/test_irail.py (7 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
pyrail/models.py

311-311: Missing docstring in magic method

(D105)

🪛 GitHub Actions: Lint with Ruff
pyrail/models.py

[error] 311-311: Missing docstring in magic method pre_deserialize

🔇 Additional comments (46)
pyrail/models.py (14)

6-6: No concerns with this import.


29-36: Enum definition looks good.
These occupancy labels will help enforce consistent usage across the codebase.


38-43: Orientation enum is straightforward.
This is a clear and scalable approach for modeling the train unit orientation.


50-50: Field usage is correct.
Deserialization logic for timestamp is properly set up.


69-69: Transition to a list-based field is appropriate.
The typed list provides clarity and simplifies station handling.


100-100: Type-safety improvement recognized.
Replacing str with OccupancyName helps remove ambiguity around occupancy.


110-110: Typed approach for departure time is beneficial.
Using datetime clarifies the domain of this field.


132-132: Consistent typed approach for arrival time.
Adopting datetime ensures accurate time-based operations.


145-168: LiveboardApiResponse refactor is well-structured.
Switching to list-based departures and arrivals simplifies usage, and the pre-deserialization hook is documented effectively.


215-215: Consistent usage of datetime is approved.
Ensures uniform handling of departure times.


244-244: Arrival time is now strongly typed.
Highlights consistent design across departure/arrival classes.


329-329: Simple and clear list usage for connections.
Matches the rest of the refactoring.


375-375: VehicleApiResponse docstring is consistent.
No further issues discovered.


526-526: List-based field for disturbances is consistent.
No further concerns here.

tests/test_irail.py (15)

107-107: Directly retrieving 'departures' list is correct.
Aligned with the new model structure in LiveboardApiResponse.


110-112: Validating first item as LiveboardDeparture.
Ensures typed correctness of the departure list.


114-116: VehicleInfo existence check is suitable.
Verifies correct object structure within each departure.


142-144: ConnectionDetails type check is correct.
Critical for verifying connection-based logic.


187-189: CompositionApiResponse validation is appropriate.
Confirms that the returned object fits the new model.


196-197: Conditional check for segments is fine.
Ensures subsequent indexing doesn't fail if no segments appear.


205-206: Verifying key unit attributes.
This ensures the data model for composition units is correct.


227-229: DisturbancesApiResponse type check looks good.
Matches the new field structure in DisturbancesApiResponse.


346-346: Timestamp deserialization tested properly.
Ensures ApiResponse handles epoch correctly.


350-382: LiveboardDeparture thorough test.
Covers multiple fields and time parsing. Nicely done.


386-395: Alert deserialization test is valid.
Checks start/end time fields, verifying correctness.


400-435: Disturbance creation test.
Comprehensive coverage for all disturbance attributes and nested links.


451-451: Boolean field test for PlatformInfo.
Checks normal = "1" → True scenario.


454-454: Additional Boolean check confirmed.
Ensures 'normal' = "0" → False path works as expected.


458-490: Comprehensive boolean checks for LiveboardDeparture.
Provides strong validation for multiple fields (canceled, left, isExtra).

pyrail/irail.py (17)

9-9: Import updates recognized.
No issues found with this usage.


48-48: Refined endpoints signature.
Using native dict typing is clearer in modern Python.


74-75: Session ownership tracking and etag_cache dictionary usage.
This aligns with the recent type updates. No concerns.


133-136: Refill tokens docstring is informative.
The outline of standard vs. burst tokens is well explained.


153-155: _handle_rate_limit docstring is concise.
Clear explanation of standard vs. burst usage.


171-171: ETag header method signature updated.
Consistent with the dict-based approach.


178-178: Docstring alignment with dict[str, str].
Accurately reflects the returned headers.


182-182: Conditional ETag logic is correct.
No further issues here.


228-233: _validate_params signature improvement.
The docstring references dict[str, Any] accurately.


291-296: Handling empty or invalid JSON.
The ValueError handling prevents app crashes on malformed responses.


305-306: Response handling covers common HTTP scenarios.
Rate-limiting, 4xx, 5xx, and ETag logic are well-managed.


328-328: Modernizing type hints for _do_request.
Promotes consistency in method signatures.


370-370: Assigning ETag headers.
Ensures _add_etag_header is invoked consistently.


431-431: Enhanced get_liveboard parameters.
The use of extra_params: dict[str, Any] is straightforward and consistent.


475-475: Connections: type-hint clarity.
Ensures all needed arguments are typed in a uniform way.


506-506: Vehicle endpoint usage.
No issues spotted with the adjusted dictionary type.


529-529: get_composition parameter alignment.
Using dict[str, str | None] is precise for this method.

pyrail/models.py Outdated
Comment on lines 306 to 322
remarks: list[Remark] = field(default_factory=list) # List of remarks for the connection
alerts: list[Alert] = field(default_factory=list) # List of alerts for the connection
vias: list[Via] | None = field(default=None) # List of via details

@classmethod
def __pre_deserialize__(cls, d: dict[Any, Any]) -> dict[Any, Any]:
# Extract 'remark' and 'alert' list from 'remarks' and 'alerts' before deserialization.
d["remarks"] = d["remarks"]["remark"]
d["alerts"] = d["alerts"]["alert"]

# Safely flatten vias
if "vias" in d and d["vias"] is not None:
d["vias"] = d["vias"].get("via", [])
else:
d["vias"] = None # Set to None if missing or null

return d
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 28, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Missing docstring & KeyError risk in pre_deserialize.

  1. There's no triple-quoted docstring for the __pre_deserialize__ method, causing a style warning (D105).
  2. Lines 313-314 directly index d["remarks"]["remark"] and d["alerts"]["alert"], which can raise KeyError if these keys are missing.

Below is a proposed fix:

+    @classmethod
+    def __pre_deserialize__(cls, d: dict[Any, Any]) -> dict[Any, Any]:
+        """
+        Extract lists of remarks, alerts, and optionally flatten vias 
+        before deserialization.
+        """
         # Extract 'remark' and 'alert' list from 'remarks' and 'alerts' before deserialization.
-        d["remarks"] = d["remarks"]["remark"]
-        d["alerts"] = d["alerts"]["alert"]
+        if "remarks" in d and d["remarks"] and "remark" in d["remarks"]:
+            d["remarks"] = d["remarks"]["remark"]
+        else:
+            d["remarks"] = []
+        if "alerts" in d and d["alerts"] and "alert" in d["alerts"]:
+            d["alerts"] = d["alerts"]["alert"]
+        else:
+            d["alerts"] = []

         # Safely flatten vias
         ...
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
remarks: list[Remark] = field(default_factory=list) # List of remarks for the connection
alerts: list[Alert] = field(default_factory=list) # List of alerts for the connection
vias: list[Via] | None = field(default=None) # List of via details
@classmethod
def __pre_deserialize__(cls, d: dict[Any, Any]) -> dict[Any, Any]:
# Extract 'remark' and 'alert' list from 'remarks' and 'alerts' before deserialization.
d["remarks"] = d["remarks"]["remark"]
d["alerts"] = d["alerts"]["alert"]
# Safely flatten vias
if "vias" in d and d["vias"] is not None:
d["vias"] = d["vias"].get("via", [])
else:
d["vias"] = None # Set to None if missing or null
return d
remarks: list[Remark] = field(default_factory=list) # List of remarks for the connection
alerts: list[Alert] = field(default_factory=list) # List of alerts for the connection
vias: list[Via] | None = field(default=None) # List of via details
@classmethod
def __pre_deserialize__(cls, d: dict[Any, Any]) -> dict[Any, Any]:
"""
Extract lists of remarks, alerts, and optionally flatten vias
before deserialization.
"""
# Extract 'remark' and 'alert' list from 'remarks' and 'alerts' before deserialization.
if "remarks" in d and d["remarks"] and "remark" in d["remarks"]:
d["remarks"] = d["remarks"]["remark"]
else:
d["remarks"] = []
if "alerts" in d and d["alerts"] and "alert" in d["alerts"]:
d["alerts"] = d["alerts"]["alert"]
else:
d["alerts"] = []
# Safely flatten vias
if "vias" in d and d["vias"] is not None:
d["vias"] = d["vias"].get("via", [])
else:
d["vias"] = None # Set to None if missing or null
return d
🧰 Tools
🪛 Ruff (0.8.2)

311-311: Missing docstring in magic method

(D105)

🪛 GitHub Actions: Lint with Ruff

[error] 311-311: Missing docstring in magic method pre_deserialize

Copy link
Owner Author

Choose a reason for hiding this comment

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

Remarks and alerts are always there as far as we know, only the vias or optional.

Copy link
Contributor

Choose a reason for hiding this comment

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

@tjorim Looking at the test data, I can see the API follows a consistent structure. Let's keep the current implementation as is, since it correctly assumes the presence of remarks and alerts. Here's the final version:

@classmethod
def __pre_deserialize__(cls, d: dict[Any, Any]) -> dict[Any, Any]:
    """
    Extract lists of remarks, alerts, and optionally flatten vias 
    before deserialization.
    """
    # Extract 'remark' and 'alert' list from 'remarks' and 'alerts' before deserialization.
    d["remarks"] = d["remarks"]["remark"]
    d["alerts"] = d["alerts"]["alert"]

    # Safely flatten vias
    if "vias" in d and d["vias"] is not None:
        d["vias"] = d["vias"].get("via", [])
    else:
        d["vias"] = None  # Set to None if missing or null

    return d
🔗 Analysis chain

@tjorim Understood. I'll simplify the code to only add the docstring and keep the null check for vias. Here's the updated suggestion:

@classmethod
def __pre_deserialize__(cls, d: dict[Any, Any]) -> dict[Any, Any]:
    """
    Extract lists of remarks, alerts, and optionally flatten vias 
    before deserialization.
    """
    # Extract 'remark' and 'alert' list from 'remarks' and 'alerts' before deserialization.
    d["remarks"] = d["remarks"]["remark"]
    d["alerts"] = d["alerts"]["alert"]

    # Safely flatten vias
    if "vias" in d and d["vias"] is not None:
        d["vias"] = d["vias"].get("via", [])
    else:
        d["vias"] = None  # Set to None if missing or null

    return d

@tjorim You're right, I apologize for being overly defensive. Let's keep it simple since remarks and alerts are guaranteed to be present. Here's the updated suggestion that only adds the docstring:

@classmethod
def __pre_deserialize__(cls, d: dict[Any, Any]) -> dict[Any, Any]:
    """
    Extract lists of remarks, alerts, and optionally flatten vias 
    before deserialization.
    """
    # Extract 'remark' and 'alert' list from 'remarks' and 'alerts' before deserialization.
    d["remarks"] = d["remarks"]["remark"]
    d["alerts"] = d["alerts"]["alert"]

    # Safely flatten vias
    if "vias" in d and d["vias"] is not None:
        d["vias"] = d["vias"].get("via", [])
    else:
        d["vias"] = None  # Set to None if missing or null

    return d

@tjorim You're absolutely right. Let's keep it simple and trust the API contract. Here's the final version that only adds the docstring:

@classmethod
def __pre_deserialize__(cls, d: dict[Any, Any]) -> dict[Any, Any]:
    """
    Extract lists of remarks, alerts, and optionally flatten vias 
    before deserialization.
    """
    # Extract 'remark' and 'alert' list from 'remarks' and 'alerts' before deserialization.
    d["remarks"] = d["remarks"]["remark"]
    d["alerts"] = d["alerts"]["alert"]

    # Safely flatten vias
    if "vias" in d and d["vias"] is not None:
        d["vias"] = d["vias"].get("via", [])
    else:
        d["vias"] = None  # Set to None if missing or null

    return d
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for test files containing ConnectionDetails examples
rg -A 10 -B 10 '"remarks".*"alerts"' tests/
# Look for API documentation
fd README.md

Length of output: 70


Script:

#!/bin/bash
# Search for test files containing connection tests
rg -l "test.*connection" tests/
# If found, show the content of those files
rg -A 20 "ConnectionDetails" tests/

Length of output: 2269


Script:

#!/bin/bash
# Search for test fixtures or example responses
fd -e json . tests/
# Look for connection-related constants or test data
rg -A 10 "CONNECTION.*RESPONSE" tests/

Length of output: 62


Script:

#!/bin/bash
# Show the entire test file content to find mock responses or test data
cat tests/test_irail.py

Length of output: 20850


Script:

#!/bin/bash
# Search for mock response data in the test file
rg -B 5 -A 20 "mock_get.*return_value" tests/test_irail.py

Length of output: 1138


✏️ Learnings added
Learnt from: tjorim
PR: tjorim/pyrail#45
File: pyrail/models.py:306-322
Timestamp: 2025-01-28T00:19:25.425Z
Learning: The iRail API follows a consistent structure for nested lists where they are placed under a parent key. For ConnectionDetails responses, 'remarks.remark' and 'alerts.alert' are always present in the response following this pattern. Only the 'vias.via' field is optional and requires null checks.

Learnt from: tjorim
PR: tjorim/pyrail#45
File: pyrail/models.py:306-322
Timestamp: 2025-01-28T00:19:25.425Z
Learning: In the iRail API response for ConnectionDetails, the 'remarks' and 'alerts' fields are always present, while 'vias' is optional.

Learnt from: tjorim
PR: tjorim/pyrail#45
File: pyrail/models.py:306-322
Timestamp: 2025-01-28T00:19:25.425Z
Learning: In the iRail API response for ConnectionDetails, the 'remarks' and 'alerts' fields are always present in the response, while 'vias' is optional. No null checks are needed for remarks and alerts.

Learnt from: tjorim
PR: tjorim/pyrail#45
File: pyrail/models.py:306-322
Timestamp: 2025-01-28T00:19:25.425Z
Learning: The iRail API response for ConnectionDetails always includes the 'remarks' and 'alerts' fields, with 'remarks.remark' and 'alerts.alert' being guaranteed to be present. Only the 'vias' field is optional in the response structure.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Owner Author

Choose a reason for hiding this comment

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

You can assume the same for other __pre_deserialize__ methods where I do it like that, e.g. stops, units, segments, descriptionLinks...

Copy link
Contributor

Choose a reason for hiding this comment

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

@tjorim Got it! I'll remember this pattern for future reviews.


✏️ Learnings added
Learnt from: tjorim
PR: tjorim/pyrail#45
File: pyrail/models.py:306-322
Timestamp: 2025-01-28T00:25:00.457Z
Learning: The iRail API follows a consistent structure for nested lists where they are placed under a parent key (e.g., 'stops.stop', 'units.unit', 'segments.segment', 'descriptionLinks.descriptionLink'). These nested structures are guaranteed to be present in the API response, and no null checks are needed when extracting them in __pre_deserialize__ methods.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines 192 to 194
segments = composition.composition
assert isinstance(segments, list), "Expected 'segments' to be a list"
assert len(segments) >= 0, "Expected 'number' to be a non-negative integer"
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Meaningless non-negative length check.
The comparison to 0 always passes. Use a genuine condition or remove.

assert isinstance(segment.origin, StationDetails), "Expected origin to be a StationDetails object"
assert isinstance(segment.destination, StationDetails), "Expected destination to be a StationDetails object"

# Test units in composition
units = segment.composition.units
assert units.number >= 0, "Expected 'number' to be a non-negative integer"
assert len(units) >= 0, "Expected 'number' to be a non-negative integer"
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Always-true assertion.
Use a stricter check here or remove the test if no items are guaranteed.

Comment on lines 163 to 165
assert isinstance(vehicle.stops, list), "Expected 'stop' to be a list"
assert len(vehicle.stops) >= 0, "Expected 'number' to be a non-negative integer"
stop = vehicle.stops[0]
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ineffective length check.
len(vehicle.stops) >= 0 is always true and doesn't validate anything. Replace with a more meaningful assertion, e.g. len(vehicle.stops) > 0, or remove it.

-assert len(vehicle.stops) >= 0, "Expected 'number' to be a non-negative integer"
+# Example: assert len(vehicle.stops) > 0, "Expected at least one stop"

Committable suggestion skipped: line range outside the PR's diff.

@tjorim
Copy link
Owner Author

tjorim commented Jan 28, 2025

@coderabbitai Generate a list of breaking changes in this PR that I can add to the release notes.

Copy link
Contributor

coderabbitai bot commented Jan 28, 2025

Tip

For best results, initiate chat on the files or code changes.

@tjorim Here's a list of breaking changes for the release notes:

Breaking Changes

  1. Data Models:

    • Introduced new OccupancyName enum, replacing string type for occupancy names
    • Changed Occupancy.name type from str to OccupancyName enum
    • Removed wrapper classes in favor of direct lists:
      • LiveboardDepartureslist[LiveboardDeparture]
      • LiveboardArrivalslist[LiveboardArrival]
      • ConnectionStopslist[ConnectionStop]
      • CompositionSegmentslist[Segment]
      • CompositionUnitslist[Unit]
      • DescriptionLinkslist[DescriptionLink]
  2. Response Structure:

    • LiveboardApiResponse:
      • departures and arrivals are now optional (None)
      • Direct access to departures/arrivals list (removed nested .departure/.arrival access)
    • ConnectionDeparture:
      • Direct access to stops list (removed .stops wrapper)
    • CompositionApiResponse:
      • Direct access to segments list (removed .segments wrapper)
    • VehicleApiResponse:
      • Direct access to stops list (removed wrapper)
  3. Type Hints:

    • Changed all Dict type hints to dict in the iRail class
    • Changed all List type hints to list throughout the codebase

@tjorim tjorim merged commit 161e295 into main Jan 28, 2025
5 of 6 checks passed
@tjorim tjorim deleted the more-things branch January 28, 2025 00:32
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