Skip to content

Commit 1979f9d

Browse files
fix: use lazy imports to speed up module import time (#505)
* fix: use lazy imports to speed up module import time (#476) **Problem:** - Initial import of `okta` module was loading 1000+ model classes and 100+ API classes eagerly - Each model had complex Pydantic validation and inheritance chains - Import time was ~2 seconds, blocking application startup - Related to issue #476 **Solution:** Implemented intelligent lazy loading across three levels: 1. **Package Level (`okta/__init__.py`):** - Lazy load API classes and models using `__getattr__` hook - Only core SDK components (ApiClient, Configuration, etc.) are eagerly loaded - Models and API classes loaded on-demand when first accessed 2. **API Level (`okta/api/__init__.py`):** - Lazy load individual API classes using `__getattr__` pattern - Thread-safe imports with locking mechanism - Maintains backward compatibility 3. **Models Level (`okta/models/__init__.py`):** - Dynamic model grouping based on discriminator relationships - Preloads dependent models together to maintain class identity consistency - Critical for Pydantic validation with polymorphic models - Groups auto-generated from OpenAPI discriminator mappings - Manual field dependencies for complex model relationships **Key Technical Changes:** - **Template Updates:** - `__init__model.mustache`: Added lazy loading with discriminator-based grouping - `__init__package.mustache`: Implemented `__getattr__` for lazy loading - `api.mustache`: Fixed Content-Type header handling for non-consumes endpoints - `model_generic.mustache`: Import discriminator children from models package for class identity - **Model Grouping Strategy:** - Auto-groups models with parent-child discriminator relationships - Manual groups for field-level dependencies (e.g., DeviceAssurance → OSVersion) - Prevents Pydantic validation errors from duplicate class instances - Thread-safe lazy loading with locking - **Generated Code Changes:** - All API classes updated with lazy import support - All model classes updated to use centralized imports for discriminator handling - Models now import from `okta.models` instead of direct file paths **Testing:** - Updated GitHub Actions workflow to run pytest - Removed outdated commented test code in test_applications_it.py - All integration tests passing with new lazy loading implementation - Thread-safe implementation prevents race conditions in concurrent imports **Performance Impact:** - Import time: ~2000ms → <100ms (20x improvement) - No runtime performance impact after initial load - Maintains full backward compatibility - Models loaded on-demand only when used **Backward Compatibility:** - All existing import patterns continue to work - `from okta.models import ModelName` still functional - `from okta import ApiClass` still functional - No breaking changes to public API * Fix PR #505 review: Thread safety, HTTP compliance, and lazy loading improvements Address all critical/high-severity issues from code review: - Fix Content-Type on GET/DELETE requests (HTTP RFC compliance) - Add thread-safe locking to package-level lazy imports - Fix model class identity via okta.models delegation - Implement true lazy loading for API classes - Enhance field dependencies documentation - Align Python 3.10+ requirements across all configs Testing: Integration tests passing, import time ~150ms (was ~2000ms) Co-authored-by: Manmohan Shaw <manmohan.shaw@okta.com>
1 parent 1d6a1c0 commit 1979f9d

40 files changed

Lines changed: 4682 additions & 6280 deletions

okta/__init__.py

Lines changed: 1938 additions & 2838 deletions
Large diffs are not rendered by default.

okta/api/__init__.py

Lines changed: 145 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -8,121 +8,149 @@
88

99
# flake8: noqa
1010

11-
# import apis into api package
12-
from okta.api.agent_pools_api import AgentPoolsApi
13-
from okta.api.api_service_integrations_api import ApiServiceIntegrationsApi
14-
from okta.api.api_token_api import ApiTokenApi
15-
from okta.api.application_api import ApplicationApi
16-
from okta.api.application_connections_api import ApplicationConnectionsApi
17-
from okta.api.application_cross_app_access_connections_api import ApplicationCrossAppAccessConnectionsApi
18-
from okta.api.application_features_api import ApplicationFeaturesApi
19-
from okta.api.application_grants_api import ApplicationGrantsApi
20-
from okta.api.application_groups_api import ApplicationGroupsApi
21-
from okta.api.application_logos_api import ApplicationLogosApi
22-
from okta.api.application_policies_api import ApplicationPoliciesApi
23-
from okta.api.application_sso_api import ApplicationSSOApi
24-
from okta.api.application_sso_credential_key_api import ApplicationSSOCredentialKeyApi
25-
from okta.api.application_sso_federated_claims_api import ApplicationSSOFederatedClaimsApi
26-
from okta.api.application_sso_public_keys_api import ApplicationSSOPublicKeysApi
27-
from okta.api.application_tokens_api import ApplicationTokensApi
28-
from okta.api.application_users_api import ApplicationUsersApi
29-
from okta.api.associated_domain_customizations_api import AssociatedDomainCustomizationsApi
30-
from okta.api.attack_protection_api import AttackProtectionApi
31-
from okta.api.authenticator_api import AuthenticatorApi
32-
from okta.api.authorization_server_api import AuthorizationServerApi
33-
from okta.api.authorization_server_assoc_api import AuthorizationServerAssocApi
34-
from okta.api.authorization_server_claims_api import AuthorizationServerClaimsApi
35-
from okta.api.authorization_server_clients_api import AuthorizationServerClientsApi
36-
from okta.api.authorization_server_keys_api import AuthorizationServerKeysApi
37-
from okta.api.authorization_server_policies_api import AuthorizationServerPoliciesApi
38-
from okta.api.authorization_server_rules_api import AuthorizationServerRulesApi
39-
from okta.api.authorization_server_scopes_api import AuthorizationServerScopesApi
40-
from okta.api.behavior_api import BehaviorApi
41-
from okta.api.brands_api import BrandsApi
42-
from okta.api.captcha_api import CAPTCHAApi
43-
from okta.api.custom_domain_api import CustomDomainApi
44-
from okta.api.custom_pages_api import CustomPagesApi
45-
from okta.api.custom_templates_api import CustomTemplatesApi
46-
from okta.api.device_api import DeviceApi
47-
from okta.api.device_assurance_api import DeviceAssuranceApi
48-
from okta.api.device_integrations_api import DeviceIntegrationsApi
49-
from okta.api.device_posture_check_api import DevicePostureCheckApi
50-
from okta.api.directories_integration_api import DirectoriesIntegrationApi
51-
from okta.api.email_customization_api import EmailCustomizationApi
52-
from okta.api.email_domain_api import EmailDomainApi
53-
from okta.api.email_server_api import EmailServerApi
54-
from okta.api.event_hook_api import EventHookApi
55-
from okta.api.feature_api import FeatureApi
56-
from okta.api.governance_bundle_api import GovernanceBundleApi
57-
from okta.api.group_api import GroupApi
58-
from okta.api.group_owner_api import GroupOwnerApi
59-
from okta.api.group_push_mapping_api import GroupPushMappingApi
60-
from okta.api.group_rule_api import GroupRuleApi
61-
from okta.api.hook_key_api import HookKeyApi
62-
from okta.api.identity_provider_api import IdentityProviderApi
63-
from okta.api.identity_provider_keys_api import IdentityProviderKeysApi
64-
from okta.api.identity_provider_signing_keys_api import IdentityProviderSigningKeysApi
65-
from okta.api.identity_provider_users_api import IdentityProviderUsersApi
66-
from okta.api.identity_source_api import IdentitySourceApi
67-
from okta.api.inline_hook_api import InlineHookApi
68-
from okta.api.linked_object_api import LinkedObjectApi
69-
from okta.api.log_stream_api import LogStreamApi
70-
from okta.api.network_zone_api import NetworkZoneApi
71-
from okta.api.o_auth2_resource_server_credentials_keys_api import OAuth2ResourceServerCredentialsKeysApi
72-
from okta.api.okta_application_settings_api import OktaApplicationSettingsApi
73-
from okta.api.okta_personal_settings_api import OktaPersonalSettingsApi
74-
from okta.api.org_creator_api import OrgCreatorApi
75-
from okta.api.org_setting_admin_api import OrgSettingAdminApi
76-
from okta.api.org_setting_communication_api import OrgSettingCommunicationApi
77-
from okta.api.org_setting_contact_api import OrgSettingContactApi
78-
from okta.api.org_setting_customization_api import OrgSettingCustomizationApi
79-
from okta.api.org_setting_general_api import OrgSettingGeneralApi
80-
from okta.api.org_setting_metadata_api import OrgSettingMetadataApi
81-
from okta.api.org_setting_support_api import OrgSettingSupportApi
82-
from okta.api.policy_api import PolicyApi
83-
from okta.api.principal_rate_limit_api import PrincipalRateLimitApi
84-
from okta.api.profile_mapping_api import ProfileMappingApi
85-
from okta.api.push_provider_api import PushProviderApi
86-
from okta.api.rate_limit_settings_api import RateLimitSettingsApi
87-
from okta.api.realm_api import RealmApi
88-
from okta.api.realm_assignment_api import RealmAssignmentApi
89-
from okta.api.role_assignment_a_user_api import RoleAssignmentAUserApi
90-
from okta.api.role_assignment_b_group_api import RoleAssignmentBGroupApi
91-
from okta.api.role_assignment_client_api import RoleAssignmentClientApi
92-
from okta.api.role_b_target_admin_api import RoleBTargetAdminApi
93-
from okta.api.role_b_target_b_group_api import RoleBTargetBGroupApi
94-
from okta.api.role_b_target_client_api import RoleBTargetClientApi
95-
from okta.api.role_c_resource_set_api import RoleCResourceSetApi
96-
from okta.api.role_c_resource_set_resource_api import RoleCResourceSetResourceApi
97-
from okta.api.role_d_resource_set_binding_api import RoleDResourceSetBindingApi
98-
from okta.api.role_d_resource_set_binding_member_api import RoleDResourceSetBindingMemberApi
99-
from okta.api.role_e_custom_api import RoleECustomApi
100-
from okta.api.role_e_custom_permission_api import RoleECustomPermissionApi
101-
from okta.api.ssf_receiver_api import SSFReceiverApi
102-
from okta.api.ssf_security_event_token_api import SSFSecurityEventTokenApi
103-
from okta.api.ssf_transmitter_api import SSFTransmitterApi
104-
from okta.api.schema_api import SchemaApi
105-
from okta.api.service_account_api import ServiceAccountApi
106-
from okta.api.session_api import SessionApi
107-
from okta.api.subscription_api import SubscriptionApi
108-
from okta.api.system_log_api import SystemLogApi
109-
from okta.api.template_api import TemplateApi
110-
from okta.api.themes_api import ThemesApi
111-
from okta.api.threat_insight_api import ThreatInsightApi
112-
from okta.api.trusted_origin_api import TrustedOriginApi
113-
from okta.api.ui_schema_api import UISchemaApi
114-
from okta.api.user_api import UserApi
115-
from okta.api.user_authenticator_enrollments_api import UserAuthenticatorEnrollmentsApi
116-
from okta.api.user_classification_api import UserClassificationApi
117-
from okta.api.user_cred_api import UserCredApi
118-
from okta.api.user_factor_api import UserFactorApi
119-
from okta.api.user_grant_api import UserGrantApi
120-
from okta.api.user_lifecycle_api import UserLifecycleApi
121-
from okta.api.user_linked_object_api import UserLinkedObjectApi
122-
from okta.api.user_o_auth_api import UserOAuthApi
123-
from okta.api.user_resources_api import UserResourcesApi
124-
from okta.api.user_risk_api import UserRiskApi
125-
from okta.api.user_sessions_api import UserSessionsApi
126-
from okta.api.user_type_api import UserTypeApi
127-
from okta.api.web_authn_preregistration_api import WebAuthnPreregistrationApi
11+
import importlib as _importlib
12+
import threading as _threading
12813

14+
# Lock for thread-safe lazy imports
15+
_import_lock = _threading.Lock()
16+
17+
# Lazy import mapping for API classes
18+
_LAZY_IMPORT_MAP = {
19+
"AgentPoolsApi": "okta.api.agent_pools_api",
20+
"ApiServiceIntegrationsApi": "okta.api.api_service_integrations_api",
21+
"ApiTokenApi": "okta.api.api_token_api",
22+
"ApplicationApi": "okta.api.application_api",
23+
"ApplicationConnectionsApi": "okta.api.application_connections_api",
24+
"ApplicationCrossAppAccessConnectionsApi": "okta.api.application_cross_app_access_connections_api",
25+
"ApplicationFeaturesApi": "okta.api.application_features_api",
26+
"ApplicationGrantsApi": "okta.api.application_grants_api",
27+
"ApplicationGroupsApi": "okta.api.application_groups_api",
28+
"ApplicationLogosApi": "okta.api.application_logos_api",
29+
"ApplicationPoliciesApi": "okta.api.application_policies_api",
30+
"ApplicationSSOApi": "okta.api.application_sso_api",
31+
"ApplicationSSOCredentialKeyApi": "okta.api.application_sso_credential_key_api",
32+
"ApplicationSSOFederatedClaimsApi": "okta.api.application_sso_federated_claims_api",
33+
"ApplicationSSOPublicKeysApi": "okta.api.application_sso_public_keys_api",
34+
"ApplicationTokensApi": "okta.api.application_tokens_api",
35+
"ApplicationUsersApi": "okta.api.application_users_api",
36+
"AssociatedDomainCustomizationsApi": "okta.api.associated_domain_customizations_api",
37+
"AttackProtectionApi": "okta.api.attack_protection_api",
38+
"AuthenticatorApi": "okta.api.authenticator_api",
39+
"AuthorizationServerApi": "okta.api.authorization_server_api",
40+
"AuthorizationServerAssocApi": "okta.api.authorization_server_assoc_api",
41+
"AuthorizationServerClaimsApi": "okta.api.authorization_server_claims_api",
42+
"AuthorizationServerClientsApi": "okta.api.authorization_server_clients_api",
43+
"AuthorizationServerKeysApi": "okta.api.authorization_server_keys_api",
44+
"AuthorizationServerPoliciesApi": "okta.api.authorization_server_policies_api",
45+
"AuthorizationServerRulesApi": "okta.api.authorization_server_rules_api",
46+
"AuthorizationServerScopesApi": "okta.api.authorization_server_scopes_api",
47+
"BehaviorApi": "okta.api.behavior_api",
48+
"BrandsApi": "okta.api.brands_api",
49+
"CAPTCHAApi": "okta.api.captcha_api",
50+
"CustomDomainApi": "okta.api.custom_domain_api",
51+
"CustomPagesApi": "okta.api.custom_pages_api",
52+
"CustomTemplatesApi": "okta.api.custom_templates_api",
53+
"DeviceApi": "okta.api.device_api",
54+
"DeviceAssuranceApi": "okta.api.device_assurance_api",
55+
"DeviceIntegrationsApi": "okta.api.device_integrations_api",
56+
"DevicePostureCheckApi": "okta.api.device_posture_check_api",
57+
"DirectoriesIntegrationApi": "okta.api.directories_integration_api",
58+
"EmailCustomizationApi": "okta.api.email_customization_api",
59+
"EmailDomainApi": "okta.api.email_domain_api",
60+
"EmailServerApi": "okta.api.email_server_api",
61+
"EventHookApi": "okta.api.event_hook_api",
62+
"FeatureApi": "okta.api.feature_api",
63+
"GovernanceBundleApi": "okta.api.governance_bundle_api",
64+
"GroupApi": "okta.api.group_api",
65+
"GroupOwnerApi": "okta.api.group_owner_api",
66+
"GroupPushMappingApi": "okta.api.group_push_mapping_api",
67+
"GroupRuleApi": "okta.api.group_rule_api",
68+
"HookKeyApi": "okta.api.hook_key_api",
69+
"IdentityProviderApi": "okta.api.identity_provider_api",
70+
"IdentityProviderKeysApi": "okta.api.identity_provider_keys_api",
71+
"IdentityProviderSigningKeysApi": "okta.api.identity_provider_signing_keys_api",
72+
"IdentityProviderUsersApi": "okta.api.identity_provider_users_api",
73+
"IdentitySourceApi": "okta.api.identity_source_api",
74+
"InlineHookApi": "okta.api.inline_hook_api",
75+
"LinkedObjectApi": "okta.api.linked_object_api",
76+
"LogStreamApi": "okta.api.log_stream_api",
77+
"NetworkZoneApi": "okta.api.network_zone_api",
78+
"OAuth2ResourceServerCredentialsKeysApi": "okta.api.o_auth2_resource_server_credentials_keys_api",
79+
"OktaApplicationSettingsApi": "okta.api.okta_application_settings_api",
80+
"OktaPersonalSettingsApi": "okta.api.okta_personal_settings_api",
81+
"OrgCreatorApi": "okta.api.org_creator_api",
82+
"OrgSettingAdminApi": "okta.api.org_setting_admin_api",
83+
"OrgSettingCommunicationApi": "okta.api.org_setting_communication_api",
84+
"OrgSettingContactApi": "okta.api.org_setting_contact_api",
85+
"OrgSettingCustomizationApi": "okta.api.org_setting_customization_api",
86+
"OrgSettingGeneralApi": "okta.api.org_setting_general_api",
87+
"OrgSettingMetadataApi": "okta.api.org_setting_metadata_api",
88+
"OrgSettingSupportApi": "okta.api.org_setting_support_api",
89+
"PolicyApi": "okta.api.policy_api",
90+
"PrincipalRateLimitApi": "okta.api.principal_rate_limit_api",
91+
"ProfileMappingApi": "okta.api.profile_mapping_api",
92+
"PushProviderApi": "okta.api.push_provider_api",
93+
"RateLimitSettingsApi": "okta.api.rate_limit_settings_api",
94+
"RealmApi": "okta.api.realm_api",
95+
"RealmAssignmentApi": "okta.api.realm_assignment_api",
96+
"RoleAssignmentAUserApi": "okta.api.role_assignment_a_user_api",
97+
"RoleAssignmentBGroupApi": "okta.api.role_assignment_b_group_api",
98+
"RoleAssignmentClientApi": "okta.api.role_assignment_client_api",
99+
"RoleBTargetAdminApi": "okta.api.role_b_target_admin_api",
100+
"RoleBTargetBGroupApi": "okta.api.role_b_target_b_group_api",
101+
"RoleBTargetClientApi": "okta.api.role_b_target_client_api",
102+
"RoleCResourceSetApi": "okta.api.role_c_resource_set_api",
103+
"RoleCResourceSetResourceApi": "okta.api.role_c_resource_set_resource_api",
104+
"RoleDResourceSetBindingApi": "okta.api.role_d_resource_set_binding_api",
105+
"RoleDResourceSetBindingMemberApi": "okta.api.role_d_resource_set_binding_member_api",
106+
"RoleECustomApi": "okta.api.role_e_custom_api",
107+
"RoleECustomPermissionApi": "okta.api.role_e_custom_permission_api",
108+
"SSFReceiverApi": "okta.api.ssf_receiver_api",
109+
"SSFSecurityEventTokenApi": "okta.api.ssf_security_event_token_api",
110+
"SSFTransmitterApi": "okta.api.ssf_transmitter_api",
111+
"SchemaApi": "okta.api.schema_api",
112+
"ServiceAccountApi": "okta.api.service_account_api",
113+
"SessionApi": "okta.api.session_api",
114+
"SubscriptionApi": "okta.api.subscription_api",
115+
"SystemLogApi": "okta.api.system_log_api",
116+
"TemplateApi": "okta.api.template_api",
117+
"ThemesApi": "okta.api.themes_api",
118+
"ThreatInsightApi": "okta.api.threat_insight_api",
119+
"TrustedOriginApi": "okta.api.trusted_origin_api",
120+
"UISchemaApi": "okta.api.ui_schema_api",
121+
"UserApi": "okta.api.user_api",
122+
"UserAuthenticatorEnrollmentsApi": "okta.api.user_authenticator_enrollments_api",
123+
"UserClassificationApi": "okta.api.user_classification_api",
124+
"UserCredApi": "okta.api.user_cred_api",
125+
"UserFactorApi": "okta.api.user_factor_api",
126+
"UserGrantApi": "okta.api.user_grant_api",
127+
"UserLifecycleApi": "okta.api.user_lifecycle_api",
128+
"UserLinkedObjectApi": "okta.api.user_linked_object_api",
129+
"UserOAuthApi": "okta.api.user_o_auth_api",
130+
"UserResourcesApi": "okta.api.user_resources_api",
131+
"UserRiskApi": "okta.api.user_risk_api",
132+
"UserSessionsApi": "okta.api.user_sessions_api",
133+
"UserTypeApi": "okta.api.user_type_api",
134+
"WebAuthnPreregistrationApi": "okta.api.web_authn_preregistration_api",
135+
136+
}
137+
138+
def __getattr__(name):
139+
"""Lazy load API classes on first access to improve import performance."""
140+
if name in _LAZY_IMPORT_MAP:
141+
# Use lock to ensure thread-safe importing and caching
142+
with _import_lock:
143+
# Check again after acquiring lock (double-checked locking pattern)
144+
if name in globals():
145+
return globals()[name]
146+
147+
module = _importlib.import_module(_LAZY_IMPORT_MAP[name])
148+
attr = getattr(module, name)
149+
# Cache in globals for subsequent access
150+
globals()[name] = attr
151+
return attr
152+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
153+
154+
def __dir__():
155+
"""Support for dir() to show available lazy-loaded attributes."""
156+
return list(_LAZY_IMPORT_MAP.keys()) + list(globals().keys())

0 commit comments

Comments
 (0)