ytdl-sub 2026.1.6__tar.gz → 2026.1.13__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/PKG-INFO +2 -2
  2. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/pyproject.toml +1 -1
  3. ytdl_sub-2026.1.13/src/ytdl_sub/__init__.py +1 -0
  4. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/script/variable_types.py +1 -9
  5. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/script.py +59 -0
  6. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/array.py +22 -1
  7. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/function.py +178 -1
  8. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/map.py +30 -1
  9. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/syntax_tree.py +30 -1
  10. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/variable_dependency.py +66 -3
  11. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/script.py +37 -2
  12. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub.egg-info/PKG-INFO +2 -2
  13. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub.egg-info/requires.txt +1 -1
  14. ytdl_sub-2026.1.6/src/ytdl_sub/__init__.py +0 -1
  15. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/LICENSE +0 -0
  16. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/README.md +0 -0
  17. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/setup.cfg +0 -0
  18. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/__init__.py +0 -0
  19. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/entrypoint.py +0 -0
  20. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/output_summary.py +0 -0
  21. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/output_transaction_log.py +0 -0
  22. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/parsers/__init__.py +0 -0
  23. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/parsers/cli_to_sub.py +0 -0
  24. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/parsers/dl.py +0 -0
  25. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/cli/parsers/main.py +0 -0
  26. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/__init__.py +0 -0
  27. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/config_file.py +0 -0
  28. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/config_validator.py +0 -0
  29. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/defaults.py +0 -0
  30. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/overrides.py +0 -0
  31. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/plugin/__init__.py +0 -0
  32. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/plugin/plugin.py +0 -0
  33. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/plugin/plugin_mapping.py +0 -0
  34. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/plugin/plugin_operation.py +0 -0
  35. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/plugin/preset_plugins.py +0 -0
  36. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/preset.py +0 -0
  37. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/preset_options.py +0 -0
  38. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/validators/__init__.py +0 -0
  39. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/validators/options.py +0 -0
  40. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/config/validators/variable_validation.py +0 -0
  41. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/__init__.py +0 -0
  42. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/info_json/__init__.py +0 -0
  43. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/info_json/info_json_downloader.py +0 -0
  44. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/source_plugin.py +0 -0
  45. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/url/__init__.py +0 -0
  46. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/url/downloader.py +0 -0
  47. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/url/validators.py +0 -0
  48. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/ytdl_options_builder.py +0 -0
  49. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/downloaders/ytdlp.py +0 -0
  50. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/__init__.py +0 -0
  51. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/base_entry.py +0 -0
  52. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/entry.py +0 -0
  53. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/entry_parent.py +0 -0
  54. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/script/__init__.py +0 -0
  55. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/script/custom_functions.py +0 -0
  56. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/script/function_scripts.py +0 -0
  57. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/script/variable_definitions.py +0 -0
  58. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/variables/__init__.py +0 -0
  59. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/entries/variables/override_variables.py +0 -0
  60. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/main.py +0 -0
  61. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/__init__.py +0 -0
  62. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/audio_extract.py +0 -0
  63. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/chapters.py +0 -0
  64. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/date_range.py +0 -0
  65. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/embed_thumbnail.py +0 -0
  66. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/file_convert.py +0 -0
  67. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/filter_exclude.py +0 -0
  68. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/filter_include.py +0 -0
  69. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/format.py +0 -0
  70. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/internal/__init__.py +0 -0
  71. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/internal/view.py +0 -0
  72. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/match_filters.py +0 -0
  73. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/music_tags.py +0 -0
  74. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/nfo_tags.py +0 -0
  75. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/output_directory_nfo_tags.py +0 -0
  76. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/split_by_chapters.py +0 -0
  77. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/square_thumbnail.py +0 -0
  78. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/static_nfo_tags.py +0 -0
  79. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/subtitles.py +0 -0
  80. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/throttle_protection.py +0 -0
  81. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/plugins/video_tags.py +0 -0
  82. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/__init__.py +0 -0
  83. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/__init__.py +0 -0
  84. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/download_deletion_options.yaml +0 -0
  85. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/filter_duration.yaml +0 -0
  86. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/filter_keywords.yaml +0 -0
  87. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/media_quality.yaml +0 -0
  88. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/players.yaml +0 -0
  89. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/throttle_protection.yaml +0 -0
  90. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/url.yaml +0 -0
  91. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/url_bilateral.yaml +0 -0
  92. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/helpers/url_categorized.yaml +0 -0
  93. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/internal/__init__.py +0 -0
  94. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/internal/view.yaml +0 -0
  95. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music/__init__.py +0 -0
  96. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music/albums_from_chapters.yaml +0 -0
  97. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music/albums_from_playlists.yaml +0 -0
  98. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music/singles.yaml +0 -0
  99. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music/soundcloud.yaml +0 -0
  100. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music_videos/__init__.py +0 -0
  101. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music_videos/music_video_base.yaml +0 -0
  102. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music_videos/music_video_extras.yaml +0 -0
  103. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/music_videos/music_videos.yaml +0 -0
  104. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/tv_show/__init__.py +0 -0
  105. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/tv_show/episode.yaml +0 -0
  106. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/tv_show/tv_show.yaml +0 -0
  107. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/tv_show/tv_show_by_date.yaml +0 -0
  108. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/prebuilt_presets/tv_show/tv_show_collection.yaml +0 -0
  109. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/__init__.py +0 -0
  110. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/__init__.py +0 -0
  111. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/array_functions.py +0 -0
  112. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/boolean_functions.py +0 -0
  113. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/conditional_functions.py +0 -0
  114. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/date_functions.py +0 -0
  115. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/error_functions.py +0 -0
  116. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/json_functions.py +0 -0
  117. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/map_functions.py +0 -0
  118. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/numeric_functions.py +0 -0
  119. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/print_functions.py +0 -0
  120. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/regex_functions.py +0 -0
  121. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/functions/string_functions.py +0 -0
  122. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/parser.py +0 -0
  123. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/script_output.py +0 -0
  124. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/__init__.py +0 -0
  125. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/resolvable.py +0 -0
  126. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/types/variable.py +0 -0
  127. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/utils/__init__.py +0 -0
  128. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/utils/exception_formatters.py +0 -0
  129. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/utils/exceptions.py +0 -0
  130. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/utils/name_validation.py +0 -0
  131. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/script/utils/type_checking.py +0 -0
  132. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/__init__.py +0 -0
  133. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/base_subscription.py +0 -0
  134. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/subscription.py +0 -0
  135. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/subscription_download.py +0 -0
  136. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/subscription_validators.py +0 -0
  137. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/subscriptions/subscription_ytdl_options.py +0 -0
  138. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/thread/__init__.py +0 -0
  139. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/thread/log_entries_downloaded_listener.py +0 -0
  140. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/__init__.py +0 -0
  141. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/chapters.py +0 -0
  142. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/datetime.py +0 -0
  143. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/exceptions.py +0 -0
  144. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/ffmpeg.py +0 -0
  145. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/file_handler.py +0 -0
  146. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/file_lock.py +0 -0
  147. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/file_path.py +0 -0
  148. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/logger.py +0 -0
  149. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/retry.py +0 -0
  150. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/scriptable.py +0 -0
  151. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/subtitles.py +0 -0
  152. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/system.py +0 -0
  153. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/thumbnail.py +0 -0
  154. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/xml.py +0 -0
  155. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/utils/yaml.py +0 -0
  156. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/__init__.py +0 -0
  157. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/audo_codec_validator.py +0 -0
  158. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/file_path_validators.py +0 -0
  159. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/nfo_validators.py +0 -0
  160. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/regex_validator.py +0 -0
  161. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/source_variable_validator.py +0 -0
  162. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/strict_dict_validator.py +0 -0
  163. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/string_datetime.py +0 -0
  164. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/string_formatter_validators.py +0 -0
  165. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/string_select_validator.py +0 -0
  166. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/validators/validators.py +0 -0
  167. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/ytdl_additions/__init__.py +0 -0
  168. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub/ytdl_additions/enhanced_download_archive.py +0 -0
  169. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub.egg-info/SOURCES.txt +0 -0
  170. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub.egg-info/dependency_links.txt +0 -0
  171. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub.egg-info/entry_points.txt +0 -0
  172. {ytdl_sub-2026.1.6 → ytdl_sub-2026.1.13}/src/ytdl_sub.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ytdl-sub
3
- Version: 2026.1.6
3
+ Version: 2026.1.13
4
4
  Summary: Automate downloading metadata generation with YoutubeDL
5
5
  Author: Jesse Bannon
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -703,7 +703,7 @@ Requires-Dist: black==24.10.0; extra == "lint"
703
703
  Requires-Dist: isort==7.0.0; extra == "lint"
704
704
  Requires-Dist: pylint==4.0.1; extra == "lint"
705
705
  Provides-Extra: docs
706
- Requires-Dist: sphinx<9,>=7; extra == "docs"
706
+ Requires-Dist: sphinx<10,>=7; extra == "docs"
707
707
  Requires-Dist: sphinx-rtd-theme<4,>=2; extra == "docs"
708
708
  Requires-Dist: sphinx-book-theme~=1.0; extra == "docs"
709
709
  Requires-Dist: sphinx-copybutton~=0.5; extra == "docs"
@@ -52,7 +52,7 @@ lint = [
52
52
  "pylint==4.0.1",
53
53
  ]
54
54
  docs = [
55
- "sphinx>=7,<9",
55
+ "sphinx>=7,<10",
56
56
  "sphinx-rtd-theme>=2,<4",
57
57
  "sphinx-book-theme~=1.0",
58
58
  "sphinx-copybutton~=0.5",
@@ -0,0 +1 @@
1
+ __pypi_version__ = "2026.01.13";__local_version__ = "2026.01.13+530d2f7"
@@ -22,7 +22,6 @@ VariableT = TypeVar("VariableT", bound="Variable")
22
22
 
23
23
 
24
24
  def _get(
25
- cast: str,
26
25
  metadata_variable_name: str,
27
26
  metadata_key: str,
28
27
  variable_name: Optional[str],
@@ -47,7 +46,7 @@ def _get(
47
46
  return as_type(
48
47
  variable_name=variable_name or metadata_key,
49
48
  metadata_key=metadata_key,
50
- definition=f"{{ %legacy_bracket_safety(%{cast}({out})) }}",
49
+ definition=f"{{ {out} }}",
51
50
  )
52
51
 
53
52
 
@@ -182,7 +181,6 @@ class MapMetadataVariable(MetadataVariable, MapVariable):
182
181
  Creates a map variable from entry metadata
183
182
  """
184
183
  return _get(
185
- "map",
186
184
  metadata_variable_name=ENTRY_METADATA_VARIABLE_NAME,
187
185
  metadata_key=metadata_key,
188
186
  variable_name=variable_name,
@@ -204,7 +202,6 @@ class ArrayMetadataVariable(MetadataVariable, ArrayVariable):
204
202
  Creates an array variable from entry metadata
205
203
  """
206
204
  return _get(
207
- "array",
208
205
  metadata_variable_name=ENTRY_METADATA_VARIABLE_NAME,
209
206
  metadata_key=metadata_key,
210
207
  variable_name=variable_name,
@@ -226,7 +223,6 @@ class StringMetadataVariable(MetadataVariable, StringVariable):
226
223
  Creates a string variable from entry metadata
227
224
  """
228
225
  return _get(
229
- "string",
230
226
  metadata_variable_name=ENTRY_METADATA_VARIABLE_NAME,
231
227
  metadata_key=metadata_key,
232
228
  variable_name=variable_name,
@@ -245,7 +241,6 @@ class StringMetadataVariable(MetadataVariable, StringVariable):
245
241
  Creates a string variable from playlist metadata
246
242
  """
247
243
  return _get(
248
- "string",
249
244
  metadata_variable_name=PLAYLIST_METADATA_VARIABLE_NAME,
250
245
  metadata_key=metadata_key,
251
246
  variable_name=variable_name,
@@ -264,7 +259,6 @@ class StringMetadataVariable(MetadataVariable, StringVariable):
264
259
  Creates a string variable from source metadata
265
260
  """
266
261
  return _get(
267
- "string",
268
262
  metadata_variable_name=SOURCE_METADATA_VARIABLE_NAME,
269
263
  metadata_key=metadata_key,
270
264
  variable_name=variable_name,
@@ -301,7 +295,6 @@ class IntegerMetadataVariable(MetadataVariable, IntegerVariable):
301
295
  Creates an int variable from entry metadata
302
296
  """
303
297
  return _get(
304
- "int",
305
298
  metadata_variable_name=ENTRY_METADATA_VARIABLE_NAME,
306
299
  metadata_key=metadata_key,
307
300
  variable_name=variable_name,
@@ -320,7 +313,6 @@ class IntegerMetadataVariable(MetadataVariable, IntegerVariable):
320
313
  Creates an int variable from playlist metadata
321
314
  """
322
315
  return _get(
323
- "int",
324
316
  metadata_variable_name=PLAYLIST_METADATA_VARIABLE_NAME,
325
317
  metadata_key=metadata_key,
326
318
  variable_name=variable_name,
@@ -1,4 +1,5 @@
1
1
  # pylint: disable=missing-raises-doc
2
+ import copy
2
3
  from typing import Dict
3
4
  from typing import List
4
5
  from typing import Optional
@@ -7,6 +8,7 @@ from typing import Set
7
8
  from ytdl_sub.script.functions import Functions
8
9
  from ytdl_sub.script.parser import parse
9
10
  from ytdl_sub.script.script_output import ScriptOutput
11
+ from ytdl_sub.script.types.resolvable import Argument
10
12
  from ytdl_sub.script.types.resolvable import BuiltInFunctionType
11
13
  from ytdl_sub.script.types.resolvable import Lambda
12
14
  from ytdl_sub.script.types.resolvable import Resolvable
@@ -14,6 +16,7 @@ from ytdl_sub.script.types.syntax_tree import ResolvedSyntaxTree
14
16
  from ytdl_sub.script.types.syntax_tree import SyntaxTree
15
17
  from ytdl_sub.script.types.variable import FunctionArgument
16
18
  from ytdl_sub.script.types.variable import Variable
19
+ from ytdl_sub.script.types.variable_dependency import VariableDependency
17
20
  from ytdl_sub.script.utils.exceptions import UNREACHABLE
18
21
  from ytdl_sub.script.utils.exceptions import CycleDetected
19
22
  from ytdl_sub.script.utils.exceptions import IncompatibleFunctionArguments
@@ -695,3 +698,59 @@ class Script:
695
698
  Names of all functions within the Script.
696
699
  """
697
700
  return set(to_function_definition_name(name) for name in self._functions.keys())
701
+
702
+ def resolve_partial(
703
+ self,
704
+ unresolvable: Optional[Set[str]] = None,
705
+ ) -> "Script":
706
+ """
707
+ Returns
708
+ -------
709
+ New (deep-copied) script that resolves inner variables as much
710
+ as possible.
711
+ """
712
+ unresolvable: Set[str] = unresolvable or {}
713
+ resolved: Dict[Variable, Resolvable] = {}
714
+ unresolved: Dict[Variable, Argument] = {
715
+ Variable(name): definition
716
+ for name, definition in self._variables.items()
717
+ if name not in unresolvable
718
+ }
719
+
720
+ partially_resolved = True
721
+ while partially_resolved:
722
+
723
+ partially_resolved = False
724
+
725
+ for variable in list(unresolved.keys()):
726
+ definition = unresolved[variable]
727
+
728
+ maybe_resolved = definition
729
+ if isinstance(definition, Variable) and definition.name not in unresolvable:
730
+ maybe_resolved = resolved.get(definition, unresolved[definition])
731
+ elif isinstance(definition, VariableDependency):
732
+ maybe_resolved = definition.partial_resolve(
733
+ resolved_variables=resolved,
734
+ unresolved_variables=unresolved,
735
+ custom_functions=self._functions,
736
+ )
737
+
738
+ if isinstance(maybe_resolved, Resolvable):
739
+ resolved[variable] = maybe_resolved
740
+ del unresolved[variable]
741
+ partially_resolved = True
742
+ else:
743
+ unresolved[variable] = maybe_resolved
744
+
745
+ # If the definition changed, then the script changed
746
+ # which means we can iterate again
747
+ partially_resolved |= definition != maybe_resolved
748
+
749
+ return copy.deepcopy(self).add_parsed(
750
+ {var_name: self._variables[var_name] for var_name in unresolvable}
751
+ | {
752
+ var.name: ResolvedSyntaxTree(ast=[definition])
753
+ for var, definition in resolved.items()
754
+ }
755
+ | {var.name: SyntaxTree(ast=[definition]) for var, definition in unresolved.items()}
756
+ )
@@ -28,7 +28,7 @@ class UnresolvedArray(_Array, VariableDependency, FutureResolvable):
28
28
  value: List[Argument]
29
29
 
30
30
  @property
31
- def _iterable_arguments(self) -> List[Argument]:
31
+ def iterable_arguments(self) -> List[Argument]:
32
32
  return self.value
33
33
 
34
34
  def resolve(
@@ -47,6 +47,27 @@ class UnresolvedArray(_Array, VariableDependency, FutureResolvable):
47
47
  ]
48
48
  )
49
49
 
50
+ def partial_resolve(
51
+ self,
52
+ resolved_variables: Dict[Variable, Resolvable],
53
+ unresolved_variables: Dict[Variable, Argument],
54
+ custom_functions: Dict[str, VariableDependency],
55
+ ) -> Argument | Resolvable:
56
+ maybe_resolvable_values, is_resolvable = VariableDependency.try_partial_resolve(
57
+ args=self.value,
58
+ resolved_variables=resolved_variables,
59
+ unresolved_variables=unresolved_variables,
60
+ custom_functions=custom_functions,
61
+ )
62
+
63
+ out = UnresolvedArray(value=maybe_resolvable_values)
64
+ if is_resolvable:
65
+ return out.resolve(
66
+ resolved_variables=resolved_variables, custom_functions=custom_functions
67
+ )
68
+
69
+ return out
70
+
50
71
  def future_resolvable_type(self) -> Type[Resolvable]:
51
72
  return Array
52
73
 
@@ -4,6 +4,7 @@ from dataclasses import dataclass
4
4
  from typing import Callable
5
5
  from typing import Dict
6
6
  from typing import List
7
+ from typing import Optional
7
8
  from typing import Type
8
9
  from typing import Union
9
10
 
@@ -11,9 +12,11 @@ from ytdl_sub.script.functions import Functions
11
12
  from ytdl_sub.script.types.array import Array
12
13
  from ytdl_sub.script.types.array import UnresolvedArray
13
14
  from ytdl_sub.script.types.resolvable import Argument
15
+ from ytdl_sub.script.types.resolvable import Boolean
14
16
  from ytdl_sub.script.types.resolvable import BuiltInFunctionType
15
17
  from ytdl_sub.script.types.resolvable import FunctionType
16
18
  from ytdl_sub.script.types.resolvable import FutureResolvable
19
+ from ytdl_sub.script.types.resolvable import Integer
17
20
  from ytdl_sub.script.types.resolvable import Lambda
18
21
  from ytdl_sub.script.types.resolvable import NamedCustomFunction
19
22
  from ytdl_sub.script.types.resolvable import Resolvable
@@ -35,7 +38,7 @@ from ytdl_sub.script.utils.type_checking import is_union
35
38
  @dataclass(frozen=True)
36
39
  class Function(FunctionType, VariableDependency, ABC):
37
40
  @property
38
- def _iterable_arguments(self) -> List[Argument]:
41
+ def iterable_arguments(self) -> List[Argument]:
39
42
  return self.args
40
43
 
41
44
 
@@ -84,6 +87,52 @@ class CustomFunction(Function, NamedCustomFunction):
84
87
  # been checked in the parser with
85
88
  raise UNREACHABLE
86
89
 
90
+ def partial_resolve(
91
+ self,
92
+ resolved_variables: Dict[Variable, Resolvable],
93
+ unresolved_variables: Dict[Variable, Argument],
94
+ custom_functions: Dict[str, VariableDependency],
95
+ ) -> Argument | Resolvable:
96
+ maybe_resolvable_args, _ = VariableDependency.try_partial_resolve(
97
+ args=self.args,
98
+ resolved_variables=resolved_variables,
99
+ unresolved_variables=unresolved_variables,
100
+ custom_functions=custom_functions,
101
+ )
102
+
103
+ for i in range(len(self.args)):
104
+ function_arg = FunctionArgument.from_idx(idx=i, custom_function_name=self.name)
105
+ function_value = maybe_resolvable_args[i]
106
+
107
+ if isinstance(function_value, Resolvable):
108
+ resolved_variables[function_arg] = function_value
109
+ else:
110
+ unresolved_variables[function_arg] = function_value
111
+
112
+ assert len(custom_functions[self.name].iterable_arguments) == 1
113
+ custom_function_definition = custom_functions[self.name].iterable_arguments[0]
114
+
115
+ maybe_resolvable_custom_function, is_resolvable = VariableDependency.try_partial_resolve(
116
+ args=[custom_function_definition],
117
+ resolved_variables=resolved_variables,
118
+ unresolved_variables=unresolved_variables,
119
+ custom_functions=custom_functions,
120
+ )
121
+
122
+ for i in range(len(self.args)):
123
+ function_arg = FunctionArgument.from_idx(idx=i, custom_function_name=self.name)
124
+
125
+ if isinstance(maybe_resolvable_args[i], Resolvable):
126
+ del resolved_variables[function_arg]
127
+ else:
128
+ del unresolved_variables[function_arg]
129
+
130
+ if is_resolvable:
131
+ return maybe_resolvable_custom_function[0]
132
+
133
+ # Did not resolve custom function arguments, do not proceed
134
+ return CustomFunction(name=self.name, args=maybe_resolvable_args)
135
+
87
136
 
88
137
  class BuiltInFunction(Function, BuiltInFunctionType):
89
138
  def validate_args(self) -> "BuiltInFunction":
@@ -312,5 +361,133 @@ class BuiltInFunction(Function, BuiltInFunctionType):
312
361
  f"Runtime error occurred when executing the function %{self.name}: {str(exc)}"
313
362
  ) from exc
314
363
 
364
+ def _partial_resolve_conditional(
365
+ self,
366
+ resolved_variables: Dict[Variable, Resolvable],
367
+ unresolved_variables: Dict[Variable, Argument],
368
+ custom_functions: Dict[str, "VariableDependency"],
369
+ ):
370
+ """
371
+ If the conditional partially resolvable enough to warrant evaluation,
372
+ perform it here.
373
+ """
374
+ if self.is_subset_of(
375
+ variables=resolved_variables, custom_function_definitions=custom_functions
376
+ ):
377
+ return self.resolve(
378
+ resolved_variables=resolved_variables,
379
+ custom_functions=custom_functions,
380
+ )
381
+
382
+ if self.name == "if":
383
+ maybe_resolvable_arg, is_resolvable = VariableDependency.try_partial_resolve(
384
+ args=[self.args[0]],
385
+ resolved_variables=resolved_variables,
386
+ unresolved_variables=unresolved_variables,
387
+ custom_functions=custom_functions,
388
+ )
389
+ if is_resolvable:
390
+ boolean_output = maybe_resolvable_arg[0]
391
+ assert isinstance(boolean_output, Boolean)
392
+ return self.args[1] if boolean_output.native else self.args[2]
393
+
394
+ if self.name == "elif":
395
+ for idx in range(0, len(self.args), 2):
396
+ maybe_resolvable_arg, is_resolvable = VariableDependency.try_partial_resolve(
397
+ args=[self.args[idx]],
398
+ resolved_variables=resolved_variables,
399
+ unresolved_variables=unresolved_variables,
400
+ custom_functions=custom_functions,
401
+ )
402
+ if is_resolvable:
403
+ boolean_output = maybe_resolvable_arg[0]
404
+ assert isinstance(boolean_output, Boolean)
405
+ if boolean_output.native:
406
+ return self.args[idx + 1]
407
+ else:
408
+ break
409
+
410
+ if self.name == "assert_then":
411
+ maybe_resolvable_arg, is_resolvable = VariableDependency.try_partial_resolve(
412
+ args=[self.args[0]],
413
+ resolved_variables=resolved_variables,
414
+ unresolved_variables=unresolved_variables,
415
+ custom_functions=custom_functions,
416
+ )
417
+ if is_resolvable:
418
+ boolean_output = maybe_resolvable_arg[0]
419
+ assert isinstance(boolean_output, Boolean)
420
+ if boolean_output.native:
421
+ return self.args[1]
422
+
423
+ return self
424
+
425
+ def _try_optimized_partial_resolve(
426
+ self,
427
+ resolved_variables: Dict[Variable, Resolvable],
428
+ unresolved_variables: Dict[Variable, Argument],
429
+ custom_functions: Dict[str, "VariableDependency"],
430
+ ) -> Optional[Argument]:
431
+ """
432
+ If a function has enough (but not all) resolved parameters to warrant a return,
433
+ perform it here.
434
+ """
435
+ if self.name == "array_at":
436
+ if (
437
+ isinstance(self.args[0], UnresolvedArray)
438
+ and isinstance(self.args[1], Integer)
439
+ and len(self.args[0].value) >= self.args[1].value
440
+ ):
441
+ maybe_resolvable_values, _ = VariableDependency.try_partial_resolve(
442
+ args=[self.args[0].value[self.args[1].value]],
443
+ resolved_variables=resolved_variables,
444
+ unresolved_variables=unresolved_variables,
445
+ custom_functions=custom_functions,
446
+ )
447
+
448
+ return maybe_resolvable_values[0]
449
+
450
+ return None
451
+
452
+ def partial_resolve(
453
+ self,
454
+ resolved_variables: Dict[Variable, Resolvable],
455
+ unresolved_variables: Dict[Variable, Argument],
456
+ custom_functions: Dict[str, VariableDependency],
457
+ ) -> Argument | Resolvable:
458
+ conditional_return_args = self.function_spec.conditional_arg_indices(
459
+ num_input_args=len(self.args)
460
+ )
461
+
462
+ if conditional_return_args:
463
+ return self._partial_resolve_conditional(
464
+ resolved_variables=resolved_variables,
465
+ unresolved_variables=unresolved_variables,
466
+ custom_functions=custom_functions,
467
+ )
468
+
469
+ if partial_resolved := self._try_optimized_partial_resolve(
470
+ resolved_variables=resolved_variables,
471
+ unresolved_variables=unresolved_variables,
472
+ custom_functions=custom_functions,
473
+ ):
474
+ return partial_resolved
475
+
476
+ maybe_resolvable_values, is_resolvable = VariableDependency.try_partial_resolve(
477
+ args=self.args,
478
+ resolved_variables=resolved_variables,
479
+ unresolved_variables=unresolved_variables,
480
+ custom_functions=custom_functions,
481
+ )
482
+
483
+ out = BuiltInFunction(name=self.name, args=maybe_resolvable_values)
484
+ if is_resolvable:
485
+ return out.resolve(
486
+ resolved_variables=resolved_variables,
487
+ custom_functions=custom_functions,
488
+ )
489
+
490
+ return out
491
+
315
492
  def __hash__(self):
316
493
  return hash((self.name, *self.args))
@@ -31,7 +31,7 @@ class UnresolvedMap(_Map, VariableDependency, FutureResolvable):
31
31
  value: Dict[Argument, Argument]
32
32
 
33
33
  @property
34
- def _iterable_arguments(self) -> List[Argument]:
34
+ def iterable_arguments(self) -> List[Argument]:
35
35
  return list(itertools.chain(*self.value.items()))
36
36
 
37
37
  def resolve(
@@ -55,6 +55,35 @@ class UnresolvedMap(_Map, VariableDependency, FutureResolvable):
55
55
 
56
56
  return Map(output)
57
57
 
58
+ def partial_resolve(
59
+ self,
60
+ resolved_variables: Dict[Variable, Resolvable],
61
+ unresolved_variables: Dict[Variable, Argument],
62
+ custom_functions: Dict[str, VariableDependency],
63
+ ) -> Argument | Resolvable:
64
+ maybe_resolvable_keys, is_keys_resolvable = VariableDependency.try_partial_resolve(
65
+ args=self.value.keys(),
66
+ resolved_variables=resolved_variables,
67
+ unresolved_variables=unresolved_variables,
68
+ custom_functions=custom_functions,
69
+ )
70
+
71
+ maybe_resolvable_values, is_values_resolvable = VariableDependency.try_partial_resolve(
72
+ args=self.value.values(),
73
+ resolved_variables=resolved_variables,
74
+ unresolved_variables=unresolved_variables,
75
+ custom_functions=custom_functions,
76
+ )
77
+
78
+ out = UnresolvedMap(value=dict(zip(maybe_resolvable_keys, maybe_resolvable_values)))
79
+ if is_keys_resolvable and is_values_resolvable:
80
+ return out.resolve(
81
+ resolved_variables=resolved_variables,
82
+ custom_functions=custom_functions,
83
+ )
84
+
85
+ return out
86
+
58
87
  def future_resolvable_type(self) -> Type[Resolvable]:
59
88
  return Map
60
89
 
@@ -3,6 +3,7 @@ from typing import Dict
3
3
  from typing import List
4
4
  from typing import Optional
5
5
 
6
+ from ytdl_sub.script.types.function import BuiltInFunction
6
7
  from ytdl_sub.script.types.resolvable import Argument
7
8
  from ytdl_sub.script.types.resolvable import Resolvable
8
9
  from ytdl_sub.script.types.resolvable import String
@@ -15,7 +16,7 @@ class SyntaxTree(VariableDependency):
15
16
  ast: List[Argument]
16
17
 
17
18
  @property
18
- def _iterable_arguments(self) -> List[Argument]:
19
+ def iterable_arguments(self) -> List[Argument]:
19
20
  return self.ast
20
21
 
21
22
  def resolve(
@@ -40,6 +41,26 @@ class SyntaxTree(VariableDependency):
40
41
  # Otherwise, to concat multiple resolved outputs, we must concat as strings
41
42
  return String("".join([str(res) for res in resolved]))
42
43
 
44
+ def partial_resolve(
45
+ self,
46
+ resolved_variables: Dict[Variable, Resolvable],
47
+ unresolved_variables: Dict[Variable, Argument],
48
+ custom_functions: Dict[str, VariableDependency],
49
+ ) -> Argument | Resolvable:
50
+ # Ensure this does not get returned as a SyntaxTree since nesting them is not supported.
51
+ maybe_resolvable_values, _ = VariableDependency.try_partial_resolve(
52
+ args=self.ast,
53
+ resolved_variables=resolved_variables,
54
+ unresolved_variables=unresolved_variables,
55
+ custom_functions=custom_functions,
56
+ )
57
+
58
+ # Mimic the above resolve behavior
59
+ if len(maybe_resolvable_values) > 1:
60
+ return BuiltInFunction(name="concat", args=maybe_resolvable_values)
61
+
62
+ return maybe_resolvable_values[0]
63
+
43
64
  @property
44
65
  def maybe_resolvable(self) -> Optional[Resolvable]:
45
66
  """
@@ -76,3 +97,11 @@ class ResolvedSyntaxTree(SyntaxTree):
76
97
  @property
77
98
  def maybe_resolvable(self) -> Optional[Resolvable]:
78
99
  return self.ast[0]
100
+
101
+ def partial_resolve(
102
+ self,
103
+ resolved_variables: Dict[Variable, Resolvable],
104
+ unresolved_variables: Dict[Variable, Argument],
105
+ custom_functions: Dict[str, VariableDependency],
106
+ ) -> Argument | Resolvable:
107
+ return self.ast[0]
@@ -5,6 +5,7 @@ from typing import Dict
5
5
  from typing import Iterable
6
6
  from typing import List
7
7
  from typing import Set
8
+ from typing import Tuple
8
9
  from typing import Type
9
10
  from typing import TypeVar
10
11
  from typing import final
@@ -27,7 +28,7 @@ TypeT = TypeVar("TypeT")
27
28
  class VariableDependency(ABC):
28
29
  @property
29
30
  @abstractmethod
30
- def _iterable_arguments(self) -> List[Argument]:
31
+ def iterable_arguments(self) -> List[Argument]:
31
32
  """
32
33
  Returns
33
34
  -------
@@ -38,7 +39,7 @@ class VariableDependency(ABC):
38
39
  self, ttype: Type[TypeT], subclass: bool = False, instance: bool = True
39
40
  ) -> List[TypeT]:
40
41
  output: List[TypeT] = []
41
- for arg in self._iterable_arguments:
42
+ for arg in self.iterable_arguments:
42
43
  if subclass and issubclass(type(arg), ttype):
43
44
  output.append(arg)
44
45
  elif instance and isinstance(arg, ttype):
@@ -104,7 +105,7 @@ class VariableDependency(ABC):
104
105
  All CustomFunctions that this depends on.
105
106
  """
106
107
  output: Set[ParsedCustomFunction] = set()
107
- for arg in self._iterable_arguments:
108
+ for arg in self.iterable_arguments:
108
109
  if isinstance(arg, NamedCustomFunction):
109
110
  if not isinstance(arg, FunctionType):
110
111
  # A NamedCustomFunction should also always be a FunctionType
@@ -138,6 +139,28 @@ class VariableDependency(ABC):
138
139
  Resolved value
139
140
  """
140
141
 
142
+ @abstractmethod
143
+ def partial_resolve(
144
+ self,
145
+ resolved_variables: Dict[Variable, Resolvable],
146
+ unresolved_variables: Dict[Variable, Argument],
147
+ custom_functions: Dict[str, "VariableDependency"],
148
+ ) -> Argument | Resolvable:
149
+ """
150
+ Parameters
151
+ ----------
152
+ resolved_variables
153
+ Lookup of variables that have been resolved
154
+ unresolved_variables
155
+ Lookup of variables that have not been resolved
156
+ custom_functions
157
+ Lookup of any custom functions that have been parsed
158
+
159
+ Returns
160
+ -------
161
+ Either a fully resolved value or partially resolved value of the same type.
162
+ """
163
+
141
164
  @classmethod
142
165
  def _resolve_argument_type(
143
166
  cls,
@@ -222,3 +245,43 @@ class VariableDependency(ABC):
222
245
  ):
223
246
  return True
224
247
  return len(self.variables.intersection(variables)) > 0
248
+
249
+ @classmethod
250
+ def try_partial_resolve(
251
+ cls,
252
+ args: Iterable[Argument],
253
+ resolved_variables: Dict[Variable, Resolvable],
254
+ unresolved_variables: Dict[Variable, Argument],
255
+ custom_functions: Dict[str, "VariableDependency"],
256
+ ) -> Tuple[List[Argument], bool]:
257
+ """
258
+ Attempts to resolve a list of arguments. Returns a tuple of them post partially resolved,
259
+ and a boolean indicating whether all of them are fully resolved.
260
+ """
261
+ maybe_resolvable_args: List[Argument] = []
262
+ is_resolvable = True
263
+ for arg in args:
264
+ maybe_resolvable_args.append(arg)
265
+
266
+ if isinstance(arg, Lambda) and arg.value in custom_functions:
267
+ if not custom_functions[arg.value].is_subset_of(
268
+ variables=resolved_variables,
269
+ custom_function_definitions=custom_functions,
270
+ ):
271
+ is_resolvable = False
272
+ elif isinstance(arg, VariableDependency):
273
+ maybe_resolvable_args[-1] = arg.partial_resolve(
274
+ resolved_variables=resolved_variables,
275
+ unresolved_variables=unresolved_variables,
276
+ custom_functions=custom_functions,
277
+ )
278
+
279
+ if not isinstance(maybe_resolvable_args[-1], Resolvable):
280
+ is_resolvable = False
281
+ elif isinstance(arg, Variable):
282
+ if arg not in resolved_variables:
283
+ is_resolvable = False
284
+ if arg in unresolved_variables:
285
+ maybe_resolvable_args[-1] = unresolved_variables[arg]
286
+
287
+ return maybe_resolvable_args, is_resolvable