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

Refactored workload API token management for better security and implemented generic API token dispenser #3154

Merged
merged 15 commits into from
Nov 13, 2024

Conversation

stefannica
Copy link
Contributor

@stefannica stefannica commented Oct 28, 2024

Describe changes

This PR heavily reworks the current mechanism of generating API tokens used by pipeline workloads to authenticate to the ZenML server API to implement additional scoping and security validation checks for generated workload API tokens and to remove the previous time-based limitations. Here are some highlights:

  • API keys are no longer exposed to the pipeline workload containers
  • workload API tokens are no longer time-restricted and can be used to run long-running pipelines without having to reconfigure the default time expiration.
  • workload API tokens can be scoped to a schedule, a pipeline run or a pipeline step run (useful for step operators). The validity of these tokens is tied to the lifecycle of the object(s) to which it is scoped:
    • schedule scoped API tokens can only be used to authenticate for as long as the schedule itself exists and is active
    • pipeline run scoped API tokens can only be used to authenticate for as long as the pipeline run exists and the pipeline run hasn't concluded (i.e. the pipeline run status is not FAILED or COMPLETED)
    • step run scoped API tokens can only be used to authenticated for as long as the pipeline run step exists and the step run hasn't concluded (i.e. the step run status is not FAILED or COMPLETED).
  • the API key scope and authorized device scope are transferred from the API token used to authorize the client to the generated workload API tokens, just as before

In addition to this, the current restrictions concerning which types of API tokens can be used to generate workload API tokens have been removed. All types of accounts and API tokens (authorized devices, service accounts, dashboard session tokens) can be used to generate API tokens. Additional workload API tokens can be generated by pipeline workloads authenticated with workload API tokens and the scoping rules are applied incrementally:

  • a schedule scoped workload API token can be used to generate workload API tokens for the same schedule and any pipeline run and step run
  • a pipeline run scoped workload API token can be used to generate workload API tokens for the same pipeline run and any step run
  • a step run scoped workload API token can only be used to generate workload API tokens for the same step run

These improvements add a layer of security to workload API tokens that was previously only implemented via time-based expiration that was difficult to work with in the context of long-running pipelines:

  • API tokens scoped for a schedule are only valid while the schedule is configured and active. If accidentally leaked, they can be invalidated by deactivating or deleting the schedule.
  • API tokens scoped for a pipeline run are only valid while the pipeline is running. Once the pipeline is completed or failed, they are no longer valid.
  • API tokens scoped for a step run are only valid while the step is running. Once the step is completed or failed, they are no longer valid.
  • in addition to the above, all generated workload tokens inherit the API key scope or authorized device scope from the current authorization context. Therefore deleting, rotating or disabling an API key or authorized device automatically invalidates all workload API tokens generated while authenticated with that API key or authorized device.

The existing api_token endpoint can now also be used to issue generic API tokens than expire after one hour and can be used to make programmatic calls to the API for automation purposes.

Pre-requisites

Please ensure you have done the following:

  • I have read the CONTRIBUTING.md document.
  • If my change requires a change to docs, I have updated the documentation accordingly.
  • I have added tests to cover my changes.
  • I have based my new branch on develop and the open PR is targeting develop. If your branch wasn't based on develop read Contribution guide on rebasing branch to develop.
  • If my changes require changes to the dashboard, these changes are communicated/requested.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Other (add details above)

Summary by CodeRabbit

  • New Features

    • Introduced a new attribute for managing the lifetime of generic API tokens.
    • Enhanced OAuth login process with additional parameters for token generation.
    • Added a logging mechanism to warn about non-expiring API tokens.
  • Bug Fixes

    • Improved security by ensuring appropriate token management based on authentication methods.
  • Documentation

    • Updated documentation to include details about the new API token lifetime attribute.

Copy link
Contributor

coderabbitai bot commented Oct 28, 2024

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough

Walkthrough

The changes introduced in this pull request include the addition of a new attribute generic_api_token_lifetime to the ServerConfiguration class, defining the lifetime of generic API tokens. A new constant, DEFAULT_ZENML_SERVER_GENERIC_API_TOKEN_LIFETIME, is also added to specify this duration. Furthermore, the get_config_environment_vars function now includes logging to warn about non-expiring API tokens. Enhancements to the OAuth login process in auth_endpoints.py allow for more flexible token management, including optional parameters for token expiration and improved security checks for token generation.

Changes

File Path Change Summary
src/zenml/config/server_config.py Added attribute generic_api_token_lifetime to ServerConfiguration. Updated docstring.
src/zenml/constants.py Added constant DEFAULT_ZENML_SERVER_GENERIC_API_TOKEN_LIFETIME = 60 * 60.
src/zenml/orchestrators/utils.py Added logging to get_config_environment_vars function to warn about non-expiring API tokens.
src/zenml/zen_server/routers/auth_endpoints.py Updated generate_access_token and api_token functions to include optional parameters for expiration and scoping. Refactored logic for token generation.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant AuthServer
    participant TokenManager

    User->>AuthServer: Request login
    AuthServer->>TokenManager: Generate access token
    TokenManager-->>AuthServer: Return access token
    AuthServer-->>User: Send access token
Loading

🐇 "In the garden, a token does bloom,
With a lifetime to chase away gloom.
Security's key, we now hold tight,
With logging and tokens, all feels right!
A hop and a skip, through code we prance,
Celebrating changes, let's take a chance!" 🥕


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

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.

@github-actions github-actions bot added internal To filter out internal PRs and issues enhancement New feature or request labels Oct 28, 2024
@htahir1
Copy link
Contributor

htahir1 commented Oct 28, 2024

@coderabbitai review

Copy link
Contributor

coderabbitai bot commented Oct 28, 2024

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

🧹 Outside diff range and nitpick comments (2)
src/zenml/orchestrators/utils.py (1)

Line range hint 113-128: Security implementation looks robust

The code properly handles different token expiration scenarios and includes appropriate security warnings. The implementation:

  1. Uses expiring tokens for regular pipeline runs
  2. Clearly warns about security implications of non-expiring tokens
  3. Guides users toward more secure service account authentication

Consider implementing a maximum lifetime limit for non-expiring tokens as an additional security measure.

src/zenml/constants.py (1)

272-272: Consider making the token lifetime configurable via environment variable.

The constant follows the naming convention and its value aligns with the PR objective. However, consider making it configurable via an environment variable, similar to how PIPELINE_API_TOKEN_EXPIRES_MINUTES is handled.

Add an environment variable and update the constant:

+ENV_ZENML_SERVER_GENERIC_API_TOKEN_LIFETIME = f"{ENV_ZENML_SERVER_PREFIX}GENERIC_API_TOKEN_LIFETIME"
-DEFAULT_ZENML_SERVER_GENERIC_API_TOKEN_LIFETIME = 60 * 60  # 1 hour
+DEFAULT_ZENML_SERVER_GENERIC_API_TOKEN_LIFETIME = handle_int_env_var(
+    ENV_ZENML_SERVER_GENERIC_API_TOKEN_LIFETIME,
+    default=60 * 60,  # 1 hour
+)
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between fa8d86d and 0758a12.

📒 Files selected for processing (4)
  • src/zenml/config/server_config.py (3 hunks)
  • src/zenml/constants.py (1 hunks)
  • src/zenml/orchestrators/utils.py (2 hunks)
  • src/zenml/zen_server/routers/auth_endpoints.py (5 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
src/zenml/config/server_config.py (1)

Pattern src/zenml/**/*.py: Review the Python code for conformity with Python best practices.

src/zenml/constants.py (1)

Pattern src/zenml/**/*.py: Review the Python code for conformity with Python best practices.

src/zenml/orchestrators/utils.py (1)

Pattern src/zenml/**/*.py: Review the Python code for conformity with Python best practices.

src/zenml/zen_server/routers/auth_endpoints.py (1)

Pattern src/zenml/**/*.py: Review the Python code for conformity with Python best practices.

🔇 Additional comments (3)
src/zenml/orchestrators/utils.py (1)

37-37: LGTM: Logger initialization follows best practices

The logger is properly initialized using __name__ as the logger name, which is the recommended approach in Python.

src/zenml/constants.py (1)

272-272: Verify the constant usage in the codebase.

Let's verify how this constant is being used in the codebase.

✅ Verification successful

Constant is properly integrated and used

The constant DEFAULT_ZENML_SERVER_GENERIC_API_TOKEN_LIFETIME is correctly defined in constants.py and appropriately used in server_config.py as a default value for the generic_api_token_lifetime configuration parameter. The usage is consistent with its intended purpose of setting a default lifetime for generic API tokens.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for usages of the new constant to ensure it's properly integrated.
rg "DEFAULT_ZENML_SERVER_GENERIC_API_TOKEN_LIFETIME"

Length of output: 320


Script:

#!/bin/bash
# Check the usage context in server_config.py
rg -A 5 -B 5 "DEFAULT_ZENML_SERVER_GENERIC_API_TOKEN_LIFETIME" src/zenml/config/server_config.py

Length of output: 948

src/zenml/config/server_config.py (1)

123-124: LGTM! Documentation is clear and follows the existing style.

The documentation for the new attribute is well-written and clearly explains its purpose.

Comment on lines 118 to 128
logger.warning(
"An API token without an expiration time will be generated "
"and used to run this pipeline on a schedule. This is very "
"insecure because the API token cannot be revoked in case "
"of potential theft without disabling the entire user "
"accountWhen deploying a pipeline on a schedule, it is "
"strongly advised to use a service account API key to "
"authenticate to the ZenML server instead of your regular "
"user account. For more information, see "
"https://docs.zenml.io/how-to/connecting-to-zenml/connect-with-a-service-account"
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo and improve warning message readability

There's a typo in the warning message where "account" and "When" are merged. Additionally, the message could be split into multiple lines for better readability.

Apply this fix:

             logger.warning(
-                "An API token without an expiration time will be generated "
-                "and used to run this pipeline on a schedule. This is very "
-                "insecure because the API token cannot be revoked in case "
-                "of potential theft without disabling the entire user "
-                "accountWhen deploying a pipeline on a schedule, it is "
-                "strongly advised to use a service account API key to "
-                "authenticate to the ZenML server instead of your regular "
-                "user account. For more information, see "
-                "https://docs.zenml.io/how-to/connecting-to-zenml/connect-with-a-service-account"
+                "An API token without an expiration time will be generated "
+                "and used to run this pipeline on a schedule.\n\n"
+                "SECURITY WARNING: This is very insecure because the API token "
+                "cannot be revoked in case of potential theft without disabling "
+                "the entire user account.\n\n"
+                "When deploying a pipeline on a schedule, it is strongly advised "
+                "to use a service account API key to authenticate to the ZenML "
+                "server instead of your regular user account.\n\n"
+                "For more information, see: "
+                "https://docs.zenml.io/how-to/connecting-to-zenml/connect-with-a-service-account"
             )
📝 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
logger.warning(
"An API token without an expiration time will be generated "
"and used to run this pipeline on a schedule. This is very "
"insecure because the API token cannot be revoked in case "
"of potential theft without disabling the entire user "
"accountWhen deploying a pipeline on a schedule, it is "
"strongly advised to use a service account API key to "
"authenticate to the ZenML server instead of your regular "
"user account. For more information, see "
"https://docs.zenml.io/how-to/connecting-to-zenml/connect-with-a-service-account"
)
logger.warning(
"An API token without an expiration time will be generated "
"and used to run this pipeline on a schedule.\n\n"
"SECURITY WARNING: This is very insecure because the API token "
"cannot be revoked in case of potential theft without disabling "
"the entire user account.\n\n"
"When deploying a pipeline on a schedule, it is strongly advised "
"to use a service account API key to authenticate to the ZenML "
"server instead of your regular user account.\n\n"
"For more information, see: "
"https://docs.zenml.io/how-to/connecting-to-zenml/connect-with-a-service-account"
)

Comment on lines 263 to 266
generic_api_token_lifetime: int = (
DEFAULT_ZENML_SERVER_GENERIC_API_TOKEN_LIFETIME
)

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add validation for the token lifetime value.

The generic_api_token_lifetime should be validated to ensure it's a positive integer to prevent potential security issues with invalid token lifetimes.

Add the following validation to the _validate_config method:

     @model_validator(mode="before")
     @classmethod
     @before_validator_handler
     def _validate_config(cls, data: Dict[str, Any]) -> Dict[str, Any]:
+        # Validate generic API token lifetime
+        if "generic_api_token_lifetime" in data:
+            lifetime = data["generic_api_token_lifetime"]
+            if not isinstance(lifetime, int) or lifetime <= 0:
+                raise ValueError(
+                    "generic_api_token_lifetime must be a positive integer"
+                )

Committable suggestion was skipped due to low confidence.


💡 Codebase verification

Add validation for maximum token lifetime

The current implementation allows setting arbitrary token lifetimes without an upper bound. The default value is 1 hour, which is a good security practice, but there's no enforcement to prevent setting much longer lifetimes. Consider adding a validation rule to limit the maximum token lifetime to a reasonable duration (e.g., 24 hours) to prevent potential security risks from long-lived tokens.

  • In src/zenml/config/server_config.py, add a validator for generic_api_token_lifetime to enforce a maximum value
🔗 Analysis chain

Consider adding a maximum lifetime limit.

For security best practices, consider adding an upper bound to the token lifetime to prevent the creation of tokens with excessive lifetimes.

Let's check if there are any existing token lifetime limits in the codebase:


Let me check if there are any validation rules for the token lifetime:


Let me check the default value for the token lifetime:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for token lifetime related constants and validations
rg -i "token.*lifetime|lifetime.*token" -g "!*.{log,md,txt}"

Length of output: 752


Script:

#!/bin/bash
# Check for any validation rules around token lifetime
ast-grep --pattern 'generic_api_token_lifetime'

# Also get the content of the server config file
cat src/zenml/config/server_config.py

Length of output: 25570


Script:

#!/bin/bash
# Check the default token lifetime value
cat src/zenml/constants.py | grep -A 1 "DEFAULT_ZENML_SERVER_GENERIC_API_TOKEN_LIFETIME"

Length of output: 157

Comment on lines 564 to 569
return generate_access_token(
user_id=token.user_id,
expires_in=expires_minutes * 60 if expires_minutes else None,
pipeline_id=pipeline_id,
schedule_id=schedule_id,
).access_token
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Passing unused pipeline_id and schedule_id to generate_access_token

In this call to generate_access_token, the parameters pipeline_id and schedule_id are provided but not utilized within the function. Since generate_access_token does not handle these parameters, this could lead to confusion or unintended behavior.

Consider removing these parameters from the function call or updating generate_access_token to handle them appropriately.

Comment on lines 196 to 201
response: Optional[Response] = None,
device: Optional[OAuthDeviceInternalResponse] = None,
api_key: Optional[APIKeyInternalResponse] = None,
expires_in: Optional[int] = None,
pipeline_id: Optional[UUID] = None,
schedule_id: Optional[UUID] = None,
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 28, 2024

Choose a reason for hiding this comment

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

⚠️ Potential issue

Unused parameters 'pipeline_id' and 'schedule_id' in generate_access_token

The parameters pipeline_id and schedule_id are added to the generate_access_token function signature but are not utilized within the function body. If these parameters are intended to scope the access token, they should be incorporated into the JWTToken or otherwise appropriately used.

Apply this diff to include pipeline_id and schedule_id in the JWTToken:

     access_token = JWTToken(
         user_id=user_id,
         device_id=device.id if device else None,
         api_key_id=api_key.id if api_key else None,
+        pipeline_id=pipeline_id,
+        schedule_id=schedule_id,
     ).encode(expires=expires)

Committable suggestion was skipped due to low confidence.

Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to be missing indeed

Copy link
Contributor

Choose a reason for hiding this comment

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

@schustmi, glad we could identify this missing part. Including pipeline_id and schedule_id in the JWTToken will ensure proper token scoping.

(^‿^)/


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Comment on lines 196 to 201
response: Optional[Response] = None,
device: Optional[OAuthDeviceInternalResponse] = None,
api_key: Optional[APIKeyInternalResponse] = None,
expires_in: Optional[int] = None,
pipeline_id: Optional[UUID] = None,
schedule_id: Optional[UUID] = None,
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to be missing indeed

src/zenml/config/server_config.py Outdated Show resolved Hide resolved
@htahir1
Copy link
Contributor

htahir1 commented Oct 29, 2024

@schustmi Small note: If we can get this PR over the line and give to aagya we might be able to do it before she leaves... not sure but just wanted to add that if you can "take this over" so to speak and its an easy win, id do that

@schustmi
Copy link
Contributor

Yeah I wanted to, but really unsure about the early return comment, I think we should probably wait for Stefan's input on this

src/zenml/orchestrators/utils.py Outdated Show resolved Hide resolved
@stefannica stefannica changed the title Endpoint to issue generic short-lived API tokens for automated tasks Updated workload API token management and generic API token dispenser Nov 11, 2024
@stefannica stefannica changed the title Updated workload API token management and generic API token dispenser Refactored workload API token management for better security and implemented generic API token dispenser Nov 11, 2024
@stefannica
Copy link
Contributor Author

@coderabbitai review

Copy link
Contributor

coderabbitai bot commented Nov 11, 2024

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@schustmi schustmi left a comment

Choose a reason for hiding this comment

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

Love the way this turned out, just one small comment from my side

)
logger.error(error)
raise CredentialsNotValid(error)

Copy link
Contributor

Choose a reason for hiding this comment

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

If I get this right, every time such a workload token is used to send a request to the server, this code is running, correct? Do you think this will cause any trouble at scale as this is running up to 3 additional database queries per request?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess I could implement a local time-based cache that would delay re-fetching the same object from the DB for up to 30 seconds. We should keep this contained to a limited number of entries though, otherwise the memory requirements will explode during high loads.

Copy link
Contributor

Choose a reason for hiding this comment

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

Potentially yeah, but not necessary for now I hope. We're considerably reducing the amounts of server requests during pipeline execution in this and the next release, which means this hopefully doesn't matter as much

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@schustmi I added a simple in-memory cache for objects that we want to cache in the ZenML server

cache = MemoryCache()
uuid_key = UUID("12345678123456781234567812345678")

cached_or_real_object = cache.get_or_cache(
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see the get_or_cache method anywhere

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, you're right, this was left behind from an older implementation version.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

self.cache.popitem(last=False) # type: ignore[call-arg]


F = Callable[[UUID], Optional[Any]]
Copy link
Contributor

Choose a reason for hiding this comment

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

I think Any already includes Optional, so this is sort of redundant. Can also leave it like this if needed for clarity though, up to you

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


# Ensure we don't exceed max capacity
while len(self.cache) > self.max_capacity:
self.cache.popitem(last=False) # type: ignore[call-arg]
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it possible to annotate self.cache: OrderedDict[...] to get rid of this type ignore?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I read that OrderedDict isn't available as a type annotation in Python 3.8 and decided to use Dict instead. But then I realized we don't support Python 3.8 anymore so... yeah, sure :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@stefannica stefannica merged commit 0cf59cb into develop Nov 13, 2024
62 of 63 checks passed
@stefannica stefannica deleted the feature/add-generic-api-token branch November 13, 2024 12:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request internal To filter out internal PRs and issues run-slow-ci
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants