Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
0cafdc5
ref(typing): promote campaign modules to precise typing contracts
joshuarli Oct 31, 2026
0dd5ddc
fix(apidocs): resolve precise container hints
joshuarli Oct 31, 2026
f90ec0d
fix(api): Reverse previous-page ordering for descending paginators
joshuarli Oct 16, 2026
0e0dad4
test(api): cover previous-page ordering for descending querysets
joshuarli Oct 31, 2026
ab45ba6
fix(apidocs): keep optional fields and object values in schemas
joshuarli Oct 31, 2026
d185f75
fix(releases): validate artifact manifest JSON shape
joshuarli Oct 31, 2026
af7c709
ref(typing): integrate split behavior fixes
joshuarli Oct 31, 2026
9eb2181
fix(releases): validate manifest files shape
joshuarli Oct 31, 2026
2bcd33b
fix(dashboards): validate threshold JSON shape
joshuarli Oct 31, 2026
a189274
fix(artifactbundle): reject non-string debug ids
joshuarli Oct 31, 2026
cd058b7
feat(typing): add diff-aware campaign gates
joshuarli Oct 31, 2026
2dd3ccb
fix(typing): count only new Any in the campaign gate
joshuarli Oct 31, 2026
1e9e791
ref(typing): justify and validate trust-boundary casts
joshuarli Oct 31, 2026
8d17ea4
ref(typing): graduate 16 touched weaklist modules
joshuarli Oct 31, 2026
d923746
ref(typing): model getsentry broadcast extensions as optional
joshuarli Oct 31, 2026
40e297b
ref(typing): remove 411 clean weaklist entries
joshuarli Oct 31, 2026
e97b207
feat(typing): add zero-weaklist campaign goal gate
joshuarli Oct 31, 2026
5399f8e
ref(typing): graduate two endpoint weaklist modules
joshuarli Oct 31, 2026
b5089e6
feat(typing): report exact and wildcard weaklist counts
joshuarli Oct 31, 2026
8b7268a
ref(typing): graduate two admin endpoint weaklist modules
joshuarli Oct 31, 2026
d2bb3b2
ref(typing): graduate opsgenie client weaklist module
joshuarli Oct 31, 2026
9cbf4aa
refactor(typing): graduate lane 2 weaklist modules
joshuarli Oct 31, 2026
18ba4f3
ref(typing): graduate lane 3 weaklist modules
joshuarli Oct 31, 2026
ad67cc8
ref(typing): graduate lane 1 weaklist modules
joshuarli Oct 31, 2026
6cdbc7f
ref(typing): remove 76 graduated lane weaklist entries
joshuarli Oct 31, 2026
3511032
ref(typing): graduate the empty list field
joshuarli Oct 31, 2026
fb091fe
ref(typing): graduate sentry package metadata module
joshuarli Oct 31, 2026
99c19d3
ref(typing): graduate culprit weaklist module
joshuarli Oct 31, 2026
ebfd3f5
fix(typing): keep graduated modules Any-free
joshuarli Oct 31, 2026
3af6fc2
refactor(typing): graduate lane 6 weaklist modules
joshuarli Oct 31, 2026
3b518c9
ref(typing): remove lane 6 weaklist entries
joshuarli Oct 31, 2026
74cd243
ref(typing): graduate lane 4 weaklist modules
joshuarli Oct 31, 2026
8ce132f
ref(typing): graduate lane 5 weaklist modules
joshuarli Oct 31, 2026
3fd4903
ref(typing): remove lane 4 and 5 weaklist entries
joshuarli Oct 31, 2026
4e28092
ref(typing): graduate API exception weaklist module
joshuarli Oct 31, 2026
c104941
ref(typing): graduate release file weaklist module
joshuarli Oct 31, 2026
a816f3b
ref(typing): graduate lane 7 weaklist modules
joshuarli Oct 31, 2026
632e996
ref(typing): graduate lane 9 weaklist modules
joshuarli Oct 31, 2026
abb3ab5
ref(typing): remove lane 7 and 9 weaklist entries
joshuarli Oct 31, 2026
0b49e4d
fix(typing): replace lane 9 Any with precise contracts
joshuarli Oct 31, 2026
8d4ff23
ref(typing): graduate lane 8 weaklist modules
joshuarli Oct 31, 2026
04cbb6f
fix(typing): ignore Any imports in the campaign gate
joshuarli Oct 31, 2026
eeafaa9
ref(typing): remove lane 8 weaklist entries
joshuarli Oct 31, 2026
b2f2144
ref(typing): remove 16 clean weaklist wildcards
joshuarli Oct 31, 2026
0e18cc6
ref(typing): remove eight clean weaklist wildcards
joshuarli Oct 31, 2026
08fd816
ref(typing): type fifteen weaklist wildcard families
joshuarli Oct 31, 2026
f7138d0
ref(typing): type seventeen weaklist wildcard families
joshuarli Oct 31, 2026
b600d9a
ref(typing): graduate lane 12 weaklist modules
joshuarli Oct 31, 2026
5e9e7e7
ref(typing): integrate lane 12 and nine wildcard families
joshuarli Oct 31, 2026
650c378
ref(typing): graduate lane 10 weaklist modules
joshuarli Oct 31, 2026
246c4ed
ref(typing): integrate lane 10 and eighteen wildcard families
joshuarli Oct 31, 2026
163110f
ref(typing): graduate lane 15 wildcard packages
joshuarli Oct 31, 2026
f9cd3e5
ref(typing): remove lane 15 wildcard entries
joshuarli Oct 31, 2026
14e2488
ref(typing): graduate lane 11 weaklist modules
joshuarli Oct 31, 2026
53aaef8
ref(typing): remove lane 11 weaklist entries
joshuarli Oct 31, 2026
2d30e43
ref(typing): graduate lane 13 wildcard packages
joshuarli Oct 31, 2026
f888675
ref(typing): graduate lane 14 wildcard packages
joshuarli Oct 31, 2026
7054bc3
ref(typing): remove lane 13 and 14 wildcard entries
joshuarli Oct 31, 2026
1c2e3de
ref(typing): graduate lane 16 wildcard packages
joshuarli Oct 31, 2026
d8e47d8
ref(typing): graduate lane 18 wildcard packages
joshuarli Oct 31, 2026
c3a9204
ref(typing): graduate lane 17 wildcard packages
joshuarli Oct 31, 2026
122e5b7
ref(typing): integrate lanes 16-18 and finish prior skips
joshuarli Oct 31, 2026
b382a23
ref(typing): graduate lane 19 wildcard packages
joshuarli Oct 31, 2026
67f46bd
ref(typing): graduate lane 20 wildcard packages
joshuarli Oct 31, 2026
ee90a11
ref(typing): graduate lane 21 wildcard packages
joshuarli Oct 31, 2026
47984d3
ref(typing): graduate lane 22 wildcard packages
joshuarli Oct 31, 2026
509569e
ref(typing): drop 349 satisfied weaklist entries
joshuarli Oct 31, 2026
9d86fe1
ref(typing): graduate lane 28 weaklist entries
joshuarli Oct 31, 2026
6eab9cc
ref(typing): integrate lane 28 weaklist entries
joshuarli Nov 1, 2026
2f6cf42
ref(typing): graduate lane 24 weaklist entries
joshuarli Nov 1, 2026
88c87b6
refactor(typing): graduate lane 23 weaklist entries
joshuarli Nov 1, 2026
9d1c93e
refactor(typing): graduate lane 25 weaklist entries
joshuarli Oct 31, 2026
8b6153d
refactor(typing): graduate lane 26 weaklist entries
joshuarli Nov 1, 2026
cbe3e6a
ref(typing): graduate lane 27 weaklist entries
joshuarli Nov 1, 2026
7816d0f
refactor(typing): graduate lane 29 weaklist entries
joshuarli Nov 1, 2026
6e73232
refactor(typing): graduate lane 30 weaklist entries
joshuarli Nov 1, 2026
b86044b
ref(typing): reconcile lane contracts for remaining weaklist entries
joshuarli Nov 1, 2026
f5a1835
ref(typing): type rule callbacks and permission test helpers
joshuarli Nov 1, 2026
04bd803
ref(typing): keep the apidocs failure fixture untyped
joshuarli Nov 1, 2026
4665b39
ref(typing): clean up typing lane 34
joshuarli Nov 1, 2026
355a96d
ref(typing): clean up typing lane 35
joshuarli Nov 1, 2026
4e635d2
refactor(typing): clean up typing lane 36
joshuarli Nov 1, 2026
8b37bc6
ref(typing): clean up typing lane 33
joshuarli Nov 1, 2026
b962108
ref(typing): clean up typing lane 37
joshuarli Nov 1, 2026
3717273
ref(typing): clean up typing lane 32
joshuarli Nov 1, 2026
fdd873b
ref(typing): clean up typing lane 38
joshuarli Nov 1, 2026
cf1b779
ref(typing): clean up typing lane 31
joshuarli Nov 1, 2026
49c03c6
refactor(typing): retire the mypy weaklist and allowlist
joshuarli Nov 1, 2026
0ea1000
fix(typing): keep API docs schema types resolvable
joshuarli Nov 1, 2026
85f2923
fix(typing): keep the mypy allowlist absent when empty
joshuarli Nov 1, 2026
e7cba43
fix(typing): repair runtime regressions from the typing pass
joshuarli Nov 1, 2026
3463e32
ref(typing): add a strict mypy ratchet
joshuarli Nov 1, 2026
535a681
ref(typing): stub and adapt untyped third-party APIs
joshuarli Nov 1, 2026
dcf069c
ref(typing): type the shared integration HTTP client
joshuarli Nov 1, 2026
e65a313
ref(typing): narrow dynamic values at runtime boundaries
joshuarli Nov 1, 2026
a86fec2
ref(typing): narrow decoded values at their boundaries
joshuarli Nov 1, 2026
3666b62
ref(typing): validate values at untyped API boundaries
joshuarli Nov 1, 2026
bdc4a0c
ref(typing): read provider and cache payload types
joshuarli Nov 1, 2026
6c0c5e8
ref(typing): narrow framework and provider return types
joshuarli Nov 1, 2026
899e62d
ref(typing): read cache and SDK values as their declared types
joshuarli Nov 1, 2026
3be071d
ref(typing): type the remaining first-party modules
joshuarli Nov 1, 2026
9173da6
ref(typing)!: enable strict mypy repo-wide
joshuarli Nov 1, 2026
cdb948a
fix(typing): preserve boundary values the strict pass narrowed
joshuarli Nov 1, 2026
66b68d5
ci(pre-commit): list changed files without an env var
joshuarli Nov 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
22 changes: 11 additions & 11 deletions .github/workflows/pre-commit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,7 @@ jobs:
uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2
with:
token: ${{ github.token }}
# Enable listing of files matching each filter.
# Paths to files will be available in `${FILTER_NAME}_files` output variable.
list-files: json

# It doesn't make sense to lint deleted files.
# Therefore we specify we are only interested in added or modified files.
filters: |
all:
- added|modified: '**/*'
frontend:
- 'static/**'
- 'tests/js/**'
Expand Down Expand Up @@ -94,10 +86,18 @@ jobs:
- name: Run prek on PR commits
id: prek
env:
ALL_FILES: ${{ steps.changes.outputs.all_files }}
GH_TOKEN: ${{ github.token }}
REPOSITORY: ${{ github.repository }}
PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
HEAD_SHA: ${{ github.sha }}
run: |
jq '.[]' --raw-output <<< "$ALL_FILES" |
xargs .venv/bin/prek run -q --files
if [[ -n "$PULL_REQUEST_NUMBER" ]]; then
gh api --paginate "repos/$REPOSITORY/pulls/$PULL_REQUEST_NUMBER/files?per_page=100" \
--jq '.[] | select(.status != "removed") | .filename'
else
git fetch --no-tags --depth=2 origin "$HEAD_SHA"
git diff --name-only --diff-filter=AM "$HEAD_SHA^" "$HEAD_SHA"
fi | xargs .venv/bin/prek run -q --files

- name: Prune suppression budgets
if: github.event_name == 'pull_request' && !cancelled() && (steps.prek.outcome == 'success' || steps.prek.outcome == 'failure') && steps.changes.outputs.frontend == 'true'
Expand Down
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ repos:
entry: python3 -m tools.mypy_helpers.prevent_weaklist_additions
files: ^pyproject\.toml$
language: python
- id: check-typing-campaign
name: check typing campaign gates
entry: python3 -m tools.mypy_helpers.check_typing_campaign
language: python
types: [python]
require_serial: true
- id: prevent-push
name: prevent pushing master
stages: [pre-push]
Expand Down
9 changes: 6 additions & 3 deletions fixtures/apidocs_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@

import orjson
from django.conf import settings
from django.http import HttpRequest
from openapi_core.contrib.django import DjangoOpenAPIRequest, DjangoOpenAPIResponse
from openapi_core.spec import Spec
from openapi_core.validation.response.validators import V30ResponseDataValidator
from rest_framework.response import Response

from sentry.services.eventstore.models import Event
from sentry.testutils.cases import APITestCase
from sentry.testutils.helpers.datetime import before_now
from sentry.testutils.skips import requires_snuba
Expand All @@ -30,7 +33,7 @@ class APIDocsTestCase(APITestCase):
def cached_schema(self) -> Spec:
return _load_schema()

def validate_schema(self, request, response):
def validate_schema(self, request: HttpRequest, response: Response) -> None:
assert 200 <= response.status_code < 300, response.status_code

if isinstance(response.data, list):
Expand All @@ -42,9 +45,9 @@ def validate_schema(self, request, response):
DjangoOpenAPIResponse(response), # type: ignore[arg-type] # Werkzeug 3.1 Headers vs Mapping
)

def create_event(self, name, **kwargs):
def create_event(self, name: str, **kwargs: object) -> Event:
# Somewhat sane default data.
data = {
data: dict[str, object] = {
"event_id": (name * 32)[:32],
"fingerprint": ["1"],
"sdk": {"version": "5.17.0", "name": "sentry.javascript.browser"},
Expand Down
2 changes: 1 addition & 1 deletion fixtures/gitlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
class GitLabTestCase(APITestCase):
provider = IntegrationProviderSlug.GITLAB.value

def setUp(self):
def setUp(self) -> None:
self.login_as(self.user)
with assume_test_silo_mode(SiloMode.CONTROL):
self.integration = Integration.objects.create(
Expand Down
32 changes: 20 additions & 12 deletions fixtures/integrations/jira/mock.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from collections.abc import Mapping
from typing import Any, cast

from fixtures.integrations.jira.stub_client import StubJiraApiClient
from fixtures.integrations.mock_service import MockService

DEFAULT_PROJECT_ID = "10000"


class MockJira(StubJiraApiClient, MockService):
def get_projects_list(self, cached: bool = True):
def get_projects_list(self, cached: bool = True) -> list[dict[str, Any]]:
"""
List all projects in the Jira data format.

Expand All @@ -25,10 +28,10 @@ def get_projects_list(self, cached: bool = True):
},
"simplified": False,
}
for project_name in self._get_project_names() + [DEFAULT_PROJECT_ID]
for project_name in [*self._get_project_names(), DEFAULT_PROJECT_ID]
]

def set_createmeta(self, project, createmeta):
def set_createmeta(self, project: str, createmeta: object) -> None:
"""
This special method lets you seed the stub data with your own metadata.
# TODO validate createmeta
Expand All @@ -39,7 +42,7 @@ def set_createmeta(self, project, createmeta):
"""
return self._set_data(project, "createmeta", createmeta)

def get_create_meta_for_project(self, project):
def get_create_meta_for_project(self, project: str) -> dict[str, Any] | None:
"""
Get the Jira "createmeta" for a project.

Expand All @@ -50,12 +53,13 @@ def get_create_meta_for_project(self, project):

createmeta = self._get_data(project, "createmeta")
if createmeta:
return createmeta
# justification: seeded fixture data is JSON written through `set_createmeta`.
return cast("dict[str, Any]", createmeta)

# Fallback to stub data
return super().get_create_meta_for_project(project)

def create_issue(self, raw_form_data):
def create_issue(self, raw_form_data: Mapping[str, object]) -> dict[str, str]:
"""
Create a new Jira issue. Currently overwrites if the issue already exists.

Expand All @@ -64,13 +68,15 @@ def create_issue(self, raw_form_data):
"""
self._throw_if_broken()

project = raw_form_data.get("project", {}).get("id")
ticket_key = self._get_new_ticket_name(project)
self._set_data(project, ticket_key, {"fields": raw_form_data})
project = cast("Mapping[str, object]", raw_form_data.get("project", {})).get("id")
# justification: Jira project ids are strings in the request fixture payload.
project_id = cast("str", project)
ticket_key = self._get_new_ticket_name(project_id)
self._set_data(project_id, ticket_key, {"fields": raw_form_data})

return {"key": ticket_key}

def get_issue(self, issue_key):
def get_issue(self, issue_key: str) -> dict[str, Any] | None:
"""
Get a saved issue from Jira.

Expand All @@ -82,10 +88,12 @@ def get_issue(self, issue_key):
if not data:
return None

data.update(
# justification: seeded fixture data is JSON written through `_set_data`.
issue = cast("dict[str, Any]", data)
issue.update(
{
"id": issue_key,
"key": issue_key,
}
)
return data
return issue
71 changes: 42 additions & 29 deletions fixtures/integrations/jira/stub_client.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from collections.abc import Mapping
from typing import Any

from fixtures.integrations.stub_service import StubService
Expand All @@ -7,59 +8,71 @@
class StubJiraApiClient(StubService):
service_name = "jira"

def get_create_meta_for_project(self, project):
response = self._get_stub_data("createmeta_response.json")
def get_create_meta_for_project(self, project: str) -> dict[str, Any] | None:
response: dict[str, Any] = self._get_stub_data("createmeta_response.json")
if project == "10001":
response["projects"][0]["id"] = "10001"
return response["projects"][0]
project_data: dict[str, Any] | None = response["projects"][0]
return project_data

def get_issue_fields(self, project_id, issue_type_id):
return self._get_stub_data("issue_fields_response.json")
def get_issue_fields(self, project_id: str, issue_type_id: str) -> dict[str, Any]:
response: dict[str, Any] = self._get_stub_data("issue_fields_response.json")
return response

def get_issue_types(self, project_id):
return self._get_stub_data("issue_types_response.json")
def get_issue_types(self, project_id: str) -> dict[str, Any]:
response: dict[str, Any] = self._get_stub_data("issue_types_response.json")
return response

def get_priorities(self):
return self._get_stub_data("priorities_response.json")
def get_priorities(self) -> list[dict[str, Any]]:
response: list[dict[str, Any]] = self._get_stub_data("priorities_response.json")
return response

def get_versions(self, project_id):
return self._get_stub_data("versions_response.json")
def get_versions(self, project_id: str) -> list[dict[str, Any]]:
response: list[dict[str, Any]] = self._get_stub_data("versions_response.json")
return response

def get_projects_paginated(self, params: dict[str, Any] | None = None):
return self._get_stub_data("projects_paginated.json")
def get_projects_paginated(self, params: dict[str, Any] | None = None) -> dict[str, Any]:
response: dict[str, Any] = self._get_stub_data("projects_paginated.json")
return response

def get_projects_list(self, cached: bool = True):
return self._get_stub_data("project_list_response.json")
def get_projects_list(self, cached: bool = True) -> list[dict[str, Any]]:
response: list[dict[str, Any]] = self._get_stub_data("project_list_response.json")
return response

def get_issue(self, issue_key):
return self._get_stub_data("get_issue_response.json")
def get_issue(self, issue_key: str) -> dict[str, Any] | None:
response: dict[str, Any] | None = self._get_stub_data("get_issue_response.json")
return response

def create_comment(self, issue_id, comment):
def create_comment(self, issue_id: str, comment: str) -> str:
return comment

def update_comment(self, issue_key, comment_id, comment):
def update_comment(self, issue_key: str, comment_id: str, comment: str) -> str:
return comment

def create_issue(self, raw_form_data):
def create_issue(self, raw_form_data: Mapping[str, Any]) -> dict[str, str]:
return {"key": "APP-123"}

def get_transitions(self, issue_key):
return self._get_stub_data("transition_response.json")["transitions"]
def get_transitions(self, issue_key: str) -> list[dict[str, Any]]:
response: dict[str, Any] = self._get_stub_data("transition_response.json")
transitions: list[dict[str, Any]] = response["transitions"]
return transitions

def transition_issue(self, issue_key, transition_id):
def transition_issue(self, issue_key: str, transition_id: str) -> None:
pass

def user_id_field(self) -> str:
return "accountId"

def get_user(self, user_id):
user = self._get_stub_data("user.json")
def get_user(self, user_id: str) -> dict[str, Any]:
user: dict[str, Any] = self._get_stub_data("user.json")
if user["accountId"] == user_id:
return user
raise ApiError("no user found")

def get_valid_statuses(self):
return self._get_stub_data("status_response.json")
def get_valid_statuses(self) -> list[dict[str, Any]]:
response: list[dict[str, Any]] = self._get_stub_data("status_response.json")
return response

def search_users_for_project(self, project, username):
return [self._get_stub_data("user.json")]
def search_users_for_project(self, project: str, username: str) -> list[dict[str, Any]]:
user: dict[str, Any] = self._get_stub_data("user.json")
return [user]
31 changes: 17 additions & 14 deletions fixtures/integrations/mock_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import shutil
from collections import defaultdict
from collections.abc import KeysView
from typing import Any

import orjson
Expand All @@ -20,24 +21,25 @@ class MockService(StubService):
Like stubs, mocks can make tests simpler and more reliable.
"""

def __init__(self, mode="memory"):
def __init__(self, mode: str = "memory") -> None:
"""
Initialize the mock instance. Wipe the previous instance's data if it exists.
"""
super().__init__()
self.mode = mode
self._next_error_code = None
self._next_ids = defaultdict(int)
self._next_error_code: int | None = None
self._next_ids: defaultdict[str, int] = defaultdict(int)
self._memory: defaultdict[str, dict[str, Any]]

if self.mode == "file":
path = os.path.join(FIXTURE_DIRECTORY, self.service_name, "data")
if os.path.exists(path):
shutil.rmtree(path)
os.makedirs(path)
else:
self._memory: dict[str, dict[str, Any]] = defaultdict(dict)
self._memory = defaultdict(dict)

def add_project(self, project):
def add_project(self, project: str) -> None:
"""
Create a new, empty project.

Expand All @@ -48,7 +50,7 @@ def add_project(self, project):
if self.mode == "file":
self._get_project_path(project)

def remove_project(self, project):
def remove_project(self, project: str) -> None:
"""
Totally wipe out a project.

Expand All @@ -60,13 +62,13 @@ def remove_project(self, project):
path = self._get_project_path(project)
shutil.rmtree(path)

def break_next_api_call(self, error_code=500):
def break_next_api_call(self, error_code: int = 500) -> None:
"""
Simulate an outage for a single API call.
"""
self._next_error_code = error_code

def _throw_if_broken(self, message_option=None):
def _throw_if_broken(self, message_option: str | None = None) -> None:
"""
See break_next_api_call.
:param message_option: What should the message be if this raises?
Expand All @@ -77,22 +79,22 @@ def _throw_if_broken(self, message_option=None):
message = message_option or f"{self.service_name} is down"
raise Exception(f"{self._next_error_code}: {message}")

def _get_project_names(self):
def _get_project_names(self) -> KeysView[str]:
return self._next_ids.keys()

def _get_new_ticket_name(self, project):
def _get_new_ticket_name(self, project: str) -> str:
counter = self._next_ids[project]
self._next_ids[project] = counter + 1

return f"{project}-{base32_encode(counter)}"

def _get_project_path(self, project):
def _get_project_path(self, project: str) -> str:
path = os.path.join(FIXTURE_DIRECTORY, self.service_name, "data", project)

os.makedirs(path, exist_ok=True)
return path

def _set_data(self, project, name, data):
def _set_data(self, project: str, name: str, data: object) -> None:
if self.mode == "memory":
if not self._memory[project]:
self._memory[project] = defaultdict()
Expand All @@ -103,7 +105,7 @@ def _set_data(self, project, name, data):
with open(path, "wb") as f:
f.write(orjson.dumps(data))

def _get_data(self, project, name):
def _get_data(self, project: str, name: str) -> object | None:
if self.mode == "memory":
if not self._memory[project]:
self._memory[project] = defaultdict()
Expand All @@ -114,4 +116,5 @@ def _get_data(self, project, name):
return None

with open(path, "rb") as f:
return orjson.loads(f.read())
data: object = orjson.loads(f.read())
return data
Loading
Loading