Generate typed attribute stubs for GalaxyAppConfiguration from config_schema.yml

config_schema.yml is the source of truth for all 460 config attributes, but
only ~50 had explicit Python type annotations in GalaxyAppConfiguration.  The
remaining attributes were typed as Any via HasDynamicProperties.__getattr__,
requiring consumers to use # type: ignore or defensive getattr() patterns.

Add a `build_config_types` action to config_manage.py that reads the schema and
emits a *AppConfigurationAttributes mixin class with type annotations for every
schema-defined attribute.  Schema types map to Python types (str/bool/int/float
→ direct, any → Any, seq → list[Any]).  Attributes with a null schema default
get `T | None`.  A per-app override dict encodes cases where post-processing in
_process_config changes the runtime type (listified filters, float cache size,
optional interactivetools_map, etc.).

GalaxyAppConfiguration and ToolShedAppConfiguration now inherit from their
respective generated mixin, making all schema attributes statically typed.
Manual overrides in GalaxyAppConfiguration continue to refine specific
attributes (HashFunctionNameEnum, timedelta, themes dicts).

Wire the new action into the Makefile config-rebuild target so generated files
stay in sync when the schema changes.

Also fix two newly-exposed type errors in the codebase:
- galaxy/dependencies/__init__.py: guard None before passing file_source_templates_config_file to exists()
- galaxy/config/__init__.py: cast use_remote_user or single_user to bool
This commit is contained in:
mvdbeek
2026-06-06 15:26:58 +02:00
parent 00ee6fce45
commit 27b262209d
12 changed files with 708 additions and 23 deletions
+4 -1
View File
@@ -116,12 +116,15 @@ config-convert-dry-run: ## convert old style galaxy ini to yaml (dry run)
config-convert: ## convert old style galaxy ini to yaml
$(CONFIG_MANAGE) convert galaxy
config-rebuild: ## Rebuild all sample YAML and RST files from config schema
config-rebuild: ## Rebuild all sample YAML, RST files, and type stubs from config schema
$(CONFIG_MANAGE) build_sample_yaml galaxy --add-comments
$(CONFIG_MANAGE) build_rst galaxy > doc/source/admin/galaxy_options.rst
$(CONFIG_MANAGE) build_config_types galaxy
$(CONFIG_MANAGE) build_sample_yaml reports --add-comments
$(CONFIG_MANAGE) build_rst reports > doc/source/admin/reports_options.rst
$(CONFIG_MANAGE) build_config_types reports
$(CONFIG_MANAGE) build_sample_yaml tool_shed --add-comments
$(CONFIG_MANAGE) build_config_types tool_shed
config-lint: ## lint galaxy YAML configuration file
$(CONFIG_MANAGE) lint galaxy
+1 -1
View File
@@ -174,7 +174,7 @@ class PSAAuthnz(IdentityProvider):
auth_pipeline = app_config.oidc_auth_pipeline or AUTH_PIPELINE
# Add extra steps to the auth pipeline if configured.
if app_config.oidc_auth_pipeline_extra:
auth_pipeline = auth_pipeline + tuple(app_config.oidc_auth_pipeline_extra)
auth_pipeline = tuple(auth_pipeline) + tuple(app_config.oidc_auth_pipeline_extra)
self.config["SOCIAL_AUTH_PIPELINE"] = auth_pipeline
self.config["DISCONNECT_PIPELINE"] = DISCONNECT_PIPELINE
self.config[setting_name("AUTHENTICATION_BACKENDS")] = (BACKENDS[provider],)
+4 -15
View File
@@ -32,6 +32,7 @@ from urllib.parse import urlparse
import yaml
from galaxy.config._galaxy_config_schema_attributes import GalaxyAppConfigurationAttributes
from galaxy.config.schema import AppSchema
from galaxy.exceptions import ConfigurationError
from galaxy.util import (
@@ -625,7 +626,7 @@ class BaseAppConfiguration(HasDynamicProperties):
class CommonConfigurationMixin:
"""Shared configuration settings code for Galaxy and ToolShed."""
sentry_dsn: str
sentry_dsn: Optional[str]
config_dict: dict[str, str]
@property
@@ -669,7 +670,7 @@ class CommonConfigurationMixin:
raise ConfigurationError(f"Unable to create missing directory: {path}\n{unicodify(e)}")
class GalaxyAppConfiguration(BaseAppConfiguration, CommonConfigurationMixin):
class GalaxyAppConfiguration(GalaxyAppConfigurationAttributes, BaseAppConfiguration, CommonConfigurationMixin):
renamed_options = {
"blacklist_file": "email_domain_blocklist_file",
"whitelist_file": "email_domain_allowlist_file",
@@ -738,7 +739,6 @@ class GalaxyAppConfiguration(BaseAppConfiguration, CommonConfigurationMixin):
}
allow_local_account_creation: bool
allowed_origin_hostnames: list[str]
builds_file_path: str
container_resolvers_config_file: str
database_connection: str
@@ -752,10 +752,8 @@ class GalaxyAppConfiguration(BaseAppConfiguration, CommonConfigurationMixin):
hash_function: HashFunctionNameEnum
integrated_tool_panel_config: str
involucro_path: str
len_file_path: str
manage_dependency_relationships: bool
monitor_thread_join_timeout: int
mulled_channels: list[str]
new_file_path: str
nginx_upload_store: str
password_expiration_period: timedelta
@@ -766,21 +764,12 @@ class GalaxyAppConfiguration(BaseAppConfiguration, CommonConfigurationMixin):
themes: dict[str, dict[str, str]]
themes_by_host: dict[str, dict[str, dict[str, str]]]
tool_data_path: str
tool_dependency_dir: Optional[str]
tool_filters: list[str]
tool_label_filters: list[str]
tool_path: str
tool_section_filters: list[str]
toolbox_filter_base_modules: list[str]
track_jobs_in_database: bool
trust_jupyter_notebook_conversion: bool
tus_upload_store: str
use_remote_user: bool
user_library_import_dir_auto_creation: bool
user_library_import_symlink_allowlist: list[str]
user_tool_filters: list[str]
user_tool_label_filters: list[str]
user_tool_section_filters: list[str]
visualization_plugins_directory: str
workflow_resource_params_mapper: str
@@ -942,7 +931,7 @@ class GalaxyAppConfiguration(BaseAppConfiguration, CommonConfigurationMixin):
raise ConfigurationError(f"Unrecognized value for hash_function option: {self.hash_function}")
self.hash_function = HashFunctionNameEnum[self.hash_function]
self.metadata_strategy = kwargs.get("metadata_strategy", "directory")
self.use_remote_user = self.use_remote_user or self.single_user
self.use_remote_user = bool(self.use_remote_user or self.single_user)
self.fetch_url_allowlist_ips = parse_allowlist_ips(listify(kwargs.get("fetch_url_allowlist")))
self.job_queue_cleanup_interval = int(kwargs.get("job_queue_cleanup_interval", "5"))
@@ -0,0 +1,473 @@
# AUTOGENERATED by config_manage.py build_config_types — do not edit manually.
# Run `make config-rebuild` to regenerate from the config schema.
from datetime import timedelta
from typing import Any
class GalaxyAppConfigurationAttributes:
"""Type annotations for schema-defined "galaxy" config attributes."""
config_dir: str
managed_config_dir: str
data_dir: str
templates_dir: str
cache_dir: str
database_connection: str | None
database_engine_option_pool_size: int
database_engine_option_max_overflow: int
database_engine_option_pool_recycle: int
database_engine_option_server_side_cursors: bool
database_query_profiling_proxy: bool
database_template: str | None
database_log_query_counts: bool
slow_query_log_threshold: float
enable_per_request_sql_debugging: bool
install_database_connection: str | None
database_auto_migrate: bool
database_wait: bool
database_wait_attempts: int
database_wait_sleep: float
history_audit_table_prune_interval: int
kombu_sqla_transport_cleanup_interval: int
file_path: str
new_file_path: str
maximum_upload_file_size: int
tool_config_file: Any
shed_tool_config_file: str
migrated_tools_config: str
integrated_tool_panel_config: str
tool_path: str
tool_dependency_dir: str | None
dependency_resolvers_config_file: str
conda_prefix: str | None
conda_exec: str | None
conda_debug: bool
conda_ensure_channels: str
conda_use_local: bool
conda_auto_install: bool
conda_auto_init: bool
conda_copy_dependencies: bool
local_conda_mapping_file: str
modules_mapping_files: str
use_cached_dependency_manager: bool
tool_dependency_cache_dir: str | None
precache_dependencies: bool
tool_sheds_config_file: str
watch_tools: str
watch_job_rules: str
watch_core_config: str
watch_tours: str
short_term_storage_dir: str
short_term_storage_default_duration: int
short_term_storage_maximum_duration: int
short_term_storage_cleanup_interval: int
bulk_storage_operation_dataset_minimum_days_to_expiration: int
bulk_storage_operation_completed_run_retention_days: int
prune_expired_bulk_storage_operations_interval: int
recover_stale_bulk_storage_operation_runs_interval: int
file_sources_config_file: str
file_sources: list[Any]
object_store_templates_config_file: str | None
object_store_templates: list[dict[str, Any]] | None
file_source_templates_config_file: str | None
file_source_templates: list[dict[str, Any]] | None
user_config_templates_use_saved_configuration: str
enable_mulled_containers: bool
container_resolvers_config_file: str | None
container_resolvers: list[Any]
involucro_path: str
involucro_auto_init: bool
mulled_channels: list[str]
enable_tool_shed_check: bool
hours_between_check: int
tool_data_table_config_path: str
shed_tool_data_table_config: str
tool_data_path: str
shed_tool_data_path: str | None
watch_tool_data_dir: str
refgenie_config_file: str | None
build_sites_config_file: str
builds_file_path: str
len_file_path: str
datatypes_config_file: str
sniff_compressed_dynamic_datatypes_default: bool
datatypes_disable_auto: bool
visualization_plugins_directory: str
tour_config_dir: str
enable_tool_generated_tours: bool
webhooks_dir: str
job_working_directory: str
template_cache_path: str
check_job_script_integrity: bool
check_job_script_integrity_count: int
check_job_script_integrity_sleep: float
default_job_shell: str
tool_search_index_dir: str
tool_tag_mappings_file: str | None
biotools_content_directory: str | None
biotools_use_api: bool
biotools_service_cache_type: str
biotools_service_cache_data_dir: str
biotools_service_cache_lock_dir: str
biotools_service_cache_url: str | None
biotools_service_cache_table_name: str
biotools_service_cache_schema_name: str | None
citation_cache_type: str
citation_cache_data_dir: str
citation_cache_lock_dir: str
citation_cache_url: str | None
citation_cache_table_name: str
citation_cache_schema_name: str | None
mulled_resolution_cache_type: str
mulled_resolution_cache_data_dir: str
mulled_resolution_cache_lock_dir: str
mulled_resolution_cache_expire: int
mulled_resolution_cache_url: str | None
mulled_resolution_cache_table_name: str
mulled_resolution_cache_schema_name: str | None
object_store_config_file: str
object_store_config: list[Any]
object_store_cache_monitor_driver: str
object_store_cache_monitor_interval: int
object_store_cache_path: str
object_store_cache_size: float
object_store_always_respect_user_selection: bool
object_store_store_by: str
smtp_server: str | None
smtp_username: str | None
smtp_password: str | None
smtp_ssl: bool
mailing_join_addr: str | None
mailing_join_subject: str
mailing_join_body: str
error_email_to: str | None
email_from: str | None
custom_activation_email_message: str | None
instance_resource_url: str | None
instance_access_url: str | None
email_domain_blocklist_file: str | None
email_domain_allowlist_file: str | None
email_ban_file: str | None
canonical_email_rules: Any
registration_warning_message: str
user_activation_on: bool
activation_grace_period: int
inactivity_box_content: str
password_expiration_period: timedelta
enable_account_interface: bool
session_duration: int
ga_code: str | None
plausible_server: str | None
plausible_domain: str | None
matomo_server: str | None
matomo_site_id: str | None
display_servers: str
enable_old_display_applications: bool
aws_estimate: bool
carbon_emission_estimates: bool
geographical_server_location_code: str
power_usage_effectiveness: float
interactivetools_enable: bool
interactivetools_upstream_proxy: bool
interactivetools_proxy_host: str | None
interactivetools_base_path: str
interactivetools_map: str | None
interactivetoolsproxy_map: str | None
interactivetools_prefix: str
retry_interactivetool_metadata_internally: bool
visualizations_visible: bool
message_box_visible: bool
message_box_content: str | None
message_box_class: str
brand: str | None
display_galaxy_brand: bool
pretty_datetime_format: str
trs_servers_config_file: str
user_preferences_extra_conf_path: str
default_locale: str
galaxy_url_prefix: str
galaxy_infrastructure_url: str
galaxy_infrastructure_web_port: int
welcome_url: str
logo_url: str
logo_src: str
logo_src_secondary: str | None
helpsite_url: str
wiki_url: str
quota_url: str
support_url: str
citation_url: str
citation_bibtex: str
release_doc_base_url: str
screencasts_url: str
terms_url: str | None
static_enabled: bool
static_cache_time: int
static_dir: str
static_dist_dir: str
static_images_dir: str
static_favicon_dir: str
static_scripts_dir: str
static_style_dir: str
static_robots_txt: str
display_chunk_size: int
apache_xsendfile: bool
nginx_x_accel_redirect_base: str | None
upstream_gzip: bool
upstream_mod_zip: bool
x_frame_options: str
nginx_upload_store: str | None
nginx_upload_path: str | None
nginx_upload_job_files_store: str | None
nginx_upload_job_files_path: str | None
tus_upload_store: str | None
tus_upload_store_job_files: str | None
chunk_upload_size: int
dynamic_proxy_manage: bool
dynamic_proxy: str
dynamic_proxy_session_map: str
dynamic_proxy_bind_port: int
dynamic_proxy_bind_ip: str
dynamic_proxy_debug: bool
dynamic_proxy_external_proxy: bool
dynamic_proxy_prefix: str
dynamic_proxy_golang_noaccess: int
dynamic_proxy_golang_clean_interval: int
dynamic_proxy_golang_docker_address: str
dynamic_proxy_golang_api_key: str | None
auto_configure_logging: bool
log_destination: str
log_rotate_size: str
log_rotate_count: int
log_level: str
logging: Any
database_engine_option_echo: bool
database_engine_option_echo_pool: bool
log_events: bool
log_actions: bool
fluent_log: bool
fluent_host: str
fluent_port: int
sanitize_all_html: bool
sanitize_allowlist_file: str
serve_xss_vulnerable_mimetypes: bool
allowed_origin_hostnames: list[str]
trust_jupyter_notebook_conversion: bool
debug: bool
use_access_logging_middleware: bool
use_lint: bool
use_profile: bool
use_printdebug: bool
monitor_thread_join_timeout: int
use_heartbeat: bool
heartbeat_interval: int
heartbeat_log: str
sentry_dsn: str | None
sentry_event_level: str
sentry_traces_sample_rate: float
sentry_client_traces_sample_rate: float
sentry_ca_certs: str | None
enable_statsd_middleware: bool
statsd_host: str | None
statsd_port: int
statsd_prefix: str
statsd_influxdb: bool
statsd_mock_calls: bool
queue_metrics_interval: int
library_import_dir: str | None
user_library_import_dir: str | None
user_library_import_dir_auto_creation: bool
user_library_import_symlink_allowlist: list[str]
user_library_import_check_permissions: bool
allow_path_paste: bool
disable_library_comptypes: str | None
tool_name_boost: float
tool_name_exact_multiplier: float
tool_id_boost: float
tool_section_boost: float
tool_description_boost: float
tool_label_boost: float
tool_stub_boost: float
tool_help_boost: float
tool_help_bm25f_k1: float
tool_search_limit: int
tool_enable_ngram_search: bool
tool_ngram_minsize: int
tool_ngram_maxsize: int
tool_ngram_factor: float
tool_test_data_directories: str
id_secret: str
use_remote_user: bool
remote_user_maildomain: str | None
remote_user_header: str
remote_user_secret: str
remote_user_logout_href: str | None
post_user_logout_href: str
normalize_remote_user_email: bool
single_user: str | None
admin_users: str | None
require_login: bool
show_welcome_with_login: bool
prefer_oidc_login: bool
allow_local_account_creation: bool
disable_local_accounts: bool
allow_user_deletion: bool
allow_user_impersonation: bool
show_user_prepopulate_form: bool
upload_from_form_button: str
allow_user_dataset_purge: bool
new_user_dataset_access_role_default_private: bool
expose_user_name: bool
expose_user_email: bool
fetch_url_allowlist: str | None
enable_beta_gdpr: bool
edam_panel_views: str
edam_toolbox_ontology_path: str
panel_views_dir: str
panel_views: list[Any]
default_panel_view: str
default_workflow_export_format: str
parallelize_workflow_scheduling_within_histories: bool
maximum_workflow_invocation_duration: int
maximum_workflow_jobs_per_scheduling_iteration: int
flush_per_n_datasets: int
max_discovered_files: int
history_local_serial_workflow_scheduling: bool
enable_oidc: bool
oidc_config_file: str
oidc_backends_config_file: str
oidc_auth_pipeline: list[Any]
oidc_auth_pipeline_extra: list[Any]
oidc_scope_prefix: str
auth_config_file: str
api_allow_run_as: str | None
bootstrap_admin_api_key: str | None
organization_name: str | None
organization_url: str | None
ga4gh_service_id: str | None
ga4gh_service_environment: str | None
enable_tool_tags: bool
enable_unique_workflow_defaults: bool
simplified_workflow_run_ui: str
simplified_workflow_run_ui_target_history: str
simplified_workflow_run_ui_job_cache: str
ftp_upload_site: str | None
ftp_upload_dir: str | None
ftp_upload_dir_identifier: str
ftp_upload_dir_template: str | None
ftp_upload_purge: bool
enable_quotas: bool
expose_dataset_path: bool
enable_tool_source_display: bool
job_metrics_config_file: str
job_metrics: list[Any]
expose_potentially_sensitive_job_metrics: bool
enable_legacy_sample_tracking_api: bool
enable_data_manager_user_view: bool
data_manager_config_file: str
shed_data_manager_config_file: str
galaxy_data_manager_data_path: str | None
job_config_file: str
job_config: Any
dependency_resolvers: list[Any]
dependency_resolution: Any
default_job_resubmission_condition: str | None
track_jobs_in_database: bool
use_tasked_jobs: bool
local_task_queue_workers: int
job_handler_monitor_sleep: float
job_runner_monitor_sleep: float
workflow_monitor_sleep: float
workflow_completion_monitor_sleep: float
calculate_dataset_hash: str
hash_function: str
metadata_strategy: str
retry_metadata_internally: bool
max_metadata_value_size: int
outputs_to_working_directory: bool
retry_job_output_collection: int
tool_evaluation_strategy: str
preserve_python_environment: str
cleanup_job: str
drmaa_external_runjob_script: str | None
drmaa_external_killjob_script: str | None
external_chown_script: str | None
real_system_username: str
environment_setup_file: str | None
enable_beta_markdown_export: bool
markdown_export_css: str
markdown_export_css_pages: str
markdown_export_css_invocation_reports: str
markdown_export_prologue: str
markdown_export_epilogue: str
markdown_export_prologue_pages: str
markdown_export_prologue_invocation_reports: str
markdown_export_epilogue_pages: str
markdown_export_epilogue_invocation_reports: str
job_resource_params_file: str
workflow_resource_params_file: str
workflow_resource_params_mapper: str | None
workflow_schedulers_config_file: str
workflow_scheduling_separate_materialization_iteration: bool
cache_user_job_count: bool
toolbox_auto_sort: bool
tool_filters: list[str]
tool_label_filters: list[str]
tool_section_filters: list[str]
user_tool_filters: list[str]
user_tool_section_filters: list[str]
user_tool_label_filters: list[str]
toolbox_filter_base_modules: list[str]
amqp_internal_connection: str | None
enable_celery_tasks: bool
enable_tool_requests: bool
celery_conf: Any
celery_user_rate_limit: float
celery_user_concurrency_limit: int
use_pbkdf2: bool
cookie_domain: str | None
select_type_workflow_threshold: int
ai_api_key: str | None
ai_api_base_url: str | None
ai_model: str
inference_services: Any
agent_model_capabilities_file: str
gtn_database_path: str
gtn_database_url: str
enable_tool_recommendations: bool
tool_recommendation_model_path: str
topk_recommendations: int
admin_tool_recommendations_path: str
overwrite_model_recommendations: bool
error_report_file: str
tool_destinations_config_file: str
welcome_directory: str
vault_config_file: str
vault_token_renewal_interval: int
url_headers_config_file: str
display_builtin_converters: bool
themes_config_file: str
enable_beacon_integration: bool
tool_training_recommendations: bool
tool_training_recommendations_link: str
tool_training_recommendations_api_url: str
citations_export_message_html: str
enable_sse_updates: bool
history_audit_monitor_poll_interval: int
enable_notification_system: bool
enable_mcp_server: bool
mcp_server_path: str
expired_notifications_cleanup_interval: int
dispatch_notifications_interval: int
help_forum_api_url: str
enable_help_forum_tool_panel_integration: bool
file_source_temp_dir: str | None
file_source_webdav_use_temp_files: bool
file_source_listings_expiry_time: int
install_tool_dependencies: bool
install_repository_dependencies: bool
install_resolver_dependencies: bool
enable_failed_jobs_working_directory_cleanup: bool
failed_jobs_working_directory_cleanup_days: int
failed_jobs_working_directory_cleanup_interval: int
enable_beta_tool_formats: bool
@@ -0,0 +1,16 @@
# AUTOGENERATED by config_manage.py build_config_types — do not edit manually.
# Run `make config-rebuild` to regenerate from the config schema.
class ReportsAppConfigurationAttributes:
"""Type annotations for schema-defined "reports" config attributes."""
log_level: str
database_connection: str
file_path: str
new_file_path: str
template_cache_path: str
use_heartbeat: bool
smtp_server: str
error_email_to: str
enable_beta_gdpr: bool
@@ -0,0 +1,97 @@
# AUTOGENERATED by config_manage.py build_config_types — do not edit manually.
# Run `make config-rebuild` to regenerate from the config schema.
from typing import Any
class ToolShedAppConfigurationAttributes:
"""Type annotations for schema-defined "tool_shed" config attributes."""
log_level: str
database_connection: str
hgweb_config_dir: str | None
hgweb_repo_prefix: str
config_hg_for_dev: str | None
tool_shed_url: str | None
file_path: str
new_file_path: str
builds_file_path: str
pretty_datetime_format: str
toolshed_search_on: bool
whoosh_index_dir: str
model_cache_dir: str
repo_name_boost: float
repo_description_boost: float
repo_long_description_boost: float
repo_homepage_url_boost: float
repo_remote_repository_url_boost: float
repo_owner_username_boost: float
categories_boost: float
tool_name_boost: float
tool_description_boost: float
tool_help_boost: float
tool_repo_owner_username: float
ga_code: str | None
plausible_server: str | None
plausible_domain: str | None
matomo_server: str | None
matomo_site_id: str | None
id_secret: str
use_remote_user: bool
remote_user_secret: str
remote_user_maildomain: str | None
remote_user_header: str
remote_user_logout_href: str | None
admin_users: str | None
require_login: bool
allow_local_account_creation: bool
allow_user_deletion: bool
smtp_server: str | None
email_from: str | None
smtp_username: str | None
smtp_password: str | None
smtp_ssl: bool
support_url: str
mailing_join_addr: str
ga4gh_service_id: str | None
organization_name: str | None
organization_url: str | None
ga4gh_service_environment: str | None
use_heartbeat: bool
enable_galaxy_flavor_docker_image: bool
message_box_visible: bool
message_box_content: str | None
message_box_class: str
static_enabled: bool
static_cache_time: int
static_dir: str
static_images_dir: str
static_favicon_dir: str
static_scripts_dir: str
static_style_dir: str
enable_beta_gdpr: bool
apache_xsendfile: bool
nginx_x_accel_redirect_base: str | None
nginx_upload_path: str | None
email_domain_blocklist_file: str | None
email_domain_allowlist_file: str | None
email_ban_file: str | None
canonical_email_rules: Any
brand: str | None
citation_cache_type: str
citation_cache_data_dir: str
citation_cache_lock_dir: str
citation_cache_url: str | None
citation_cache_table_name: str
citation_cache_schema_name: str | None
log_actions: bool
password_expiration_period: int
sentry_dsn: str | None
sentry_event_level: str
sentry_traces_sample_rate: float
sentry_ca_certs: str | None
session_duration: int
terms_url: str | None
auth_config_file: str
bootstrap_admin_api_key: str | None
shed_tool_data_table_config: str
datatypes_config_file: str
+107
View File
@@ -8,6 +8,7 @@ from argparse import (
)
from collections.abc import Callable
from io import StringIO
from pathlib import Path
from textwrap import TextWrapper
from typing import (
Any,
@@ -532,12 +533,118 @@ def _get_option_desc(option: dict[str, Any]) -> str:
return desc
_SCHEMA_TO_PYTHON_TYPE: dict[str, str] = {
"str": "str",
"bool": "bool",
"int": "int",
"float": "float",
"any": "Any",
"seq": "list[Any]",
}
_CONFIG_TYPE_CLASS_NAMES: dict[str, str] = {
"galaxy": "GalaxyAppConfigurationAttributes",
"tool_shed": "ToolShedAppConfigurationAttributes",
"reports": "ReportsAppConfigurationAttributes",
}
# Per-app overrides for attributes whose runtime Python type differs from what
# the schema alone would generate. Causes include: post-processing in
# _process_config (listify, timedelta conversion), BaseAppConfiguration
# guarantees that override a null schema default, or more specific element
# types for seq attrs.
_ATTR_TYPE_OVERRIDES: dict[str, dict[str, str]] = {
"galaxy": {
# Always resolved to a concrete str by BaseAppConfiguration._set_config_base
"config_dir": "str",
"data_dir": "str",
"managed_config_dir": "str",
# BaseAppConfiguration declares str; always non-null at runtime
"object_store_store_by": "str",
# Listified by _process_config or CommonConfigurationMixin
"allowed_origin_hostnames": "list[str]",
"mulled_channels": "list[str]",
"tool_filters": "list[str]",
"tool_label_filters": "list[str]",
"tool_section_filters": "list[str]",
"toolbox_filter_base_modules": "list[str]",
"user_library_import_symlink_allowlist": "list[str]",
"user_tool_filters": "list[str]",
"user_tool_label_filters": "list[str]",
"user_tool_section_filters": "list[str]",
# Can be conditionally set to None in _process_config despite non-null schema default
"interactivetools_map": "str | None",
"tool_dependency_dir": "str | None",
# Config file paths that are optional (not required to be set)
"file_source_templates_config_file": "str | None",
"object_store_templates_config_file": "str | None",
"amqp_internal_connection": "str | None",
# seq attrs with more specific element types
"file_source_templates": "list[dict[str, Any]] | None",
"object_store_templates": "list[dict[str, Any]] | None",
# Stored as float despite int schema type
"object_store_cache_size": "float",
# Converted from int (days) to timedelta by _process_config
"password_expiration_period": "timedelta",
},
"tool_shed": {},
"reports": {},
}
_CONFIG_DIR = Path(__file__).resolve().parent
def _python_type_for_option(option: dict[str, Any]) -> str:
schema_type = option.get("type", "str")
default = option.get("default")
py_type = _SCHEMA_TO_PYTHON_TYPE.get(schema_type, "Any")
# Attributes with a null default remain None at runtime when not configured —
# _update_raw_config_from_kwargs skips type conversion when value is None.
if default is None and py_type in ("str", "int", "float"):
return f"{py_type} | None"
return py_type
def _build_config_types(args: Namespace, app_desc: App) -> None:
schema = app_desc.schema
app_name = app_desc.app_name
class_name = _CONFIG_TYPE_CLASS_NAMES[app_name]
output_path = _CONFIG_DIR / f"_{app_name}_config_schema_attributes.py"
overrides = _ATTR_TYPE_OVERRIDES.get(app_name, {})
attr_types = {key: overrides.get(key, _python_type_for_option(option)) for key, option in schema.app_schema.items()}
needs_any = any("Any" in t for t in attr_types.values())
needs_timedelta = any("timedelta" in t for t in attr_types.values())
imports = ["from datetime import timedelta"] if needs_timedelta else []
if needs_any:
imports.append("from typing import Any")
lines = [
"# AUTOGENERATED by config_manage.py build_config_types — do not edit manually.",
"# Run `make config-rebuild` to regenerate from the config schema.",
*imports,
"",
"",
f"class {class_name}:",
f' """Type annotations for schema-defined "{app_name}" config attributes."""',
"",
]
for key, py_type in attr_types.items():
lines.append(f" {key}: {py_type}")
lines.append("")
content = "\n".join(lines)
output_path.write_text(content)
print(f"Written: {output_path}")
ACTIONS: dict[str, Callable] = {
"convert": _run_conversion,
"build_sample_yaml": _build_sample_yaml,
"validate": _validate,
"lint": _lint,
"build_rst": _to_rst,
"build_config_types": _build_config_types,
}
+1 -1
View File
@@ -1,5 +1,5 @@
# When committing this file, make sure to run make config-rebuild to rebuild
# all sample YAML and RST files, and add those to your commit.
# all sample YAML, RST files, and type stubs, and add those to your commit.
type: map
desc: |
Galaxy is configured by default to be usable in a single-user development
+1 -1
View File
@@ -161,7 +161,7 @@ class ConditionalDependencies:
# Parse file source templates config
file_source_templates_conf_yml = self.config_object.file_source_templates_config_file
if exists(file_source_templates_conf_yml):
if file_source_templates_conf_yml and exists(file_source_templates_conf_yml):
with open(file_source_templates_conf_yml) as f:
file_source_templates_conf = apply_syntactic_sugar(yaml.safe_load(f))
for file_source_template in file_source_templates_conf:
+1 -2
View File
@@ -1,6 +1,5 @@
from typing import (
Any,
Dict,
List,
Optional,
Union,
@@ -28,7 +27,7 @@ class AppInfo:
library_import_dir: Optional[str] = None,
enable_mulled_containers: bool = False,
container_resolvers_config_file: Optional[str] = None,
container_resolvers_config_dict: Optional[Dict[str, Any]] = None,
container_resolvers_config_dict: Optional[List[Any]] = None,
involucro_path: Optional[str] = None,
involucro_auto_init: bool = True,
mulled_channels: List[str] = DEFAULT_CHANNELS,
+2 -1
View File
@@ -15,6 +15,7 @@ from galaxy.config import (
get_database_engine_options,
TOOL_SHED_CONFIG_SCHEMA_PATH,
)
from galaxy.config._tool_shed_config_schema_attributes import ToolShedAppConfigurationAttributes
from galaxy.config.schema import AppSchema
from galaxy.exceptions import ConfigurationError
from galaxy.util import string_as_bool
@@ -29,7 +30,7 @@ log = logging.getLogger(__name__)
TOOLSHED_APP_NAME = "tool_shed"
class ToolShedAppConfiguration(BaseAppConfiguration, CommonConfigurationMixin):
class ToolShedAppConfiguration(ToolShedAppConfigurationAttributes, BaseAppConfiguration, CommonConfigurationMixin):
default_config_file_name = "tool_shed.yml"
add_sample_file_to_defaults = {"datatypes_config_file"}
+1 -1
View File
@@ -27,7 +27,7 @@ class TestErrorReporter(TestCase, UsesApp):
self.tmp_path = Path(tempfile.mkdtemp())
self.email_path = self.tmp_path / "email.json"
smtp_server = f"mock_emails_to_path://{self.email_path}"
self.app.config.smtp_server = smtp_server # type: ignore[attr-defined]
self.app.config.smtp_server = smtp_server
self.app.workflow_manager = mock.MagicMock()
def tearDown(self):