diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b43c3ec..198e568 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,4 +25,5 @@ jobs: bazel test //coverage/tests:all - name: Run rules_score tests run: | - bazel test //bazel/rules/rules_score/... + cd bazel/rules/rules_score/test + bazel test //... diff --git a/MODULE.bazel b/MODULE.bazel index e6c032d..c25d9f4 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -22,6 +22,7 @@ module( ############################################################################### bazel_dep(name = "rules_python", version = "1.4.1") bazel_dep(name = "aspect_rules_py", version = "1.4.0") +bazel_dep(name = "platforms", version = "1.0.0") bazel_dep(name = "aspect_rules_lint", version = "1.5.3") bazel_dep(name = "rules_shell", version = "0.5.0") bazel_dep(name = "rules_java", version = "8.15.1") @@ -94,7 +95,6 @@ multitool.hub( ) use_repo(multitool, "yamlfmt_hub") -bazel_dep(name = "score_docs_as_code", version = "3.0.1", dev_dependency = True) - -# bazel_dep(name = "score_platform", version = "0.5.0") -bazel_dep(name = "score_process", version = "1.3.2") +register_toolchains( + "//bazel/rules/rules_score:sphinx_default_toolchain", +) diff --git a/bazel/rules/rules_score/BUILD b/bazel/rules/rules_score/BUILD index 6b53f2f..9e030b0 100644 --- a/bazel/rules/rules_score/BUILD +++ b/bazel/rules/rules_score/BUILD @@ -1,3 +1,8 @@ +load("@aspect_rules_py//py:defs.bzl", "py_binary", "py_library") +load( + "@score_tooling//bazel/rules/rules_score:sphinx_toolchain.bzl", + "sphinx_toolchain", +) load( "//bazel/rules/rules_score:rules_score.bzl", "sphinx_module", @@ -8,46 +13,79 @@ exports_files([ "templates/seooc_index.template.rst", "templates/unit.template.rst", "templates/component.template.rst", + "src/sphinx_wrapper.py", ]) +sphinx_module( + name = "rules_score_doc", + srcs = glob( + [ + "docs/**/*.rst", + "docs/**/*.puml", + ], + allow_empty = True, + ), + index = "docs/index.rst", + visibility = ["//visibility:public"], + deps = [ + # "@score_process//:score_process_module", + ], +) + # HTML merge tool py_binary( name = "sphinx_html_merge", srcs = ["src/sphinx_html_merge.py"], - main = "src/sphinx_html_merge.py", + main = "src/sphinx_wrapper.py", + visibility = ["//visibility:public"], +) + +py_library( + name = "sphinx_module_ext", + srcs = ["src/sphinx_module_ext.py"], + imports = ["src"], visibility = ["//visibility:public"], ) # Sphinx build binary with all required dependencies py_binary( - name = "score_build", + name = "sphinx_build", srcs = ["src/sphinx_wrapper.py"], - data = [], - env = { - "SOURCE_DIRECTORY": "", - "DATA": "", - "ACTION": "check", - }, main = "src/sphinx_wrapper.py", visibility = ["//visibility:public"], deps = [ - "@score_docs_as_code//src:plantuml_for_python", - "@score_docs_as_code//src/extensions/score_sphinx_bundle", + ":sphinx_module_ext", + "@pip_tooling//myst_parser", + "@pip_tooling//sphinx", + "@pip_tooling//sphinx_design", + "@pip_tooling//sphinx_needs", + "@pip_tooling//sphinx_rtd_theme", + "@pip_tooling//sphinxcontrib_plantuml", ], ) -sphinx_module( - name = "rules_score_doc", - srcs = glob( - [ - "docs/**/*.rst", - "docs/**/*.puml", - ], - allow_empty = True, - ), - index = "docs/index.rst", +sphinx_toolchain( + name = "default_toolchain", + conf_template = "@score_tooling//bazel/rules/rules_score:templates/conf.template.py", + html_merge_tool = ":sphinx_html_merge", + sphinx = ":sphinx_build", +) + +toolchain_type( + name = "toolchain_type", visibility = ["//visibility:public"], - deps = [ - "@score_process//:score_process_module", +) + +toolchain( + name = "sphinx_default_toolchain", + exec_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + target_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", ], + toolchain = ":default_toolchain", + toolchain_type = ":toolchain_type", ) diff --git a/bazel/rules/rules_score/docs/index.rst b/bazel/rules/rules_score/docs/index.rst index 79769a4..58a42e1 100644 --- a/bazel/rules/rules_score/docs/index.rst +++ b/bazel/rules/rules_score/docs/index.rst @@ -35,164 +35,118 @@ The ``rules_score`` package provides Bazel rules for structuring and documenting All rules support cross-module dependencies for automatic sphinx-needs integration and HTML merging. -sphinx_module -------------- - -Builds Sphinx-based HTML documentation from RST source files with support for dependencies and cross-referencing. - -.. code-block:: python - - sphinx_module( - name = "my_docs", - srcs = glob(["docs/**/*.rst"]), - index = "docs/index.rst", - deps = ["@external_module//:docs"], - ) - -**Key Parameters:** - -- ``srcs``: RST/MD source files -- ``index``: Main index.rst file -- ``deps``: Other sphinx_module or dependable_element targets for cross-referencing -- ``sphinx``: Sphinx build binary (default: ``//bazel/rules/rules_score:score_build``) - -**Output:** ``/html/`` with merged dependency documentation - - -Artifact Rules --------------- - -Artifact rules define S-CORE process work products. All provide ``SphinxSourcesInfo`` for documentation generation. - -**feature_requirements** - -.. code-block:: python - - feature_requirements( - name = "features", - srcs = ["docs/features.rst"], - ) - -**component_requirements** - -.. code-block:: python - - component_requirements( - name = "requirements", - srcs = ["docs/requirements.rst"], - ) - -**assumptions_of_use** +Toolchain Configuration +----------------------- -.. code-block:: python +The ``sphinx_toolchain`` rule allows you to configure the Sphinx build environment with custom extensions. External modules must define and register their own toolchain to use ``rules_score``. - assumptions_of_use( - name = "aous", - srcs = ["docs/assumptions.rst"], - ) +**Setting Up Toolchain in External Module** -**architectural_design** +In your MODULE.bazel: .. code-block:: python - architectural_design( - name = "architecture", - static = ["docs/static_arch.rst"], - dynamic = ["docs/dynamic_arch.rst"], - ) + # Add rules_score dependency + bazel_dep(name = "score_tooling", version = "1.3.2") + + # Add dependencies for custom Sphinx extensions (if needed) + bazel_dep(name = "score_docs_as_code", version = "3.0.1") + + # Register your custom toolchain + register_toolchains("//:my_toolchain") -**safety_analysis** +In your BUILD file: .. code-block:: python - safety_analysis( - name = "safety", - controlmeasures = ["docs/controls.rst"], - failuremodes = ["docs/failures.rst"], - fta = ["docs/fta.rst"], - arch_design = ":architecture", + load("@aspect_rules_py//py:defs.bzl", "py_binary") + load("@score_tooling//bazel/rules/rules_score:sphinx_toolchain.bzl", "sphinx_toolchain") + + # Define Sphinx binary with your required extensions + py_binary( + name = "score_build", + srcs = ["@score_tooling//bazel/rules/rules_score:src/sphinx_wrapper.py"], + main = "@score_tooling//bazel/rules/rules_score:src/sphinx_wrapper.py", + visibility = ["//visibility:public"], + deps = [ + "@score_tooling//bazel/rules/rules_score:sphinx_module_ext", + "@score_docs_as_code//src:plantuml_for_python", + "@score_docs_as_code//src/extensions/score_sphinx_bundle", + # Add your custom Sphinx extensions here + ], ) -**dependability_analysis** - -.. code-block:: python - - dependability_analysis( - name = "analysis", - arch_design = ":architecture", - dfa = ["docs/dfa.rst"], - safety_analysis = [":safety"], + # Create toolchain instance + sphinx_toolchain( + name = "score_sphinx_toolchain", + sphinx = ":score_build", ) - -Structural Rules ----------------- - -**unit** - -Define the smallest testable software element. - -.. code-block:: python - - unit( - name = "my_unit", - unit_design = [":architecture"], - implementation = ["//src:lib"], - tests = ["//tests:unit_test"], + # Register as Bazel toolchain + toolchain( + name = "my_toolchain", + exec_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + target_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + toolchain = ":score_sphinx_toolchain", + toolchain_type = "@score_tooling//bazel/rules/rules_score:toolchain_type", + visibility = ["//visibility:public"], ) -**component** +**sphinx_toolchain Parameters:** -Define a collection of units. +- ``sphinx``: Label to Sphinx build binary (mandatory) +- ``conf_template``: Label to conf.py template file (optional, default: ``@score_tooling//bazel/rules/rules_score:templates/conf.template.py``) +- ``html_merge_tool``: Label to HTML merge tool (optional, default: ``@score_tooling//bazel/rules/rules_score:sphinx_html_merge``) -.. code-block:: python - - component( - name = "my_component", - component_requirements = [":requirements"], - units = [":my_unit"], - implementation = ["//src:binary"], - tests = ["//tests:integration_test"], - ) -**dependable_element** +sphinx_module +------------- -Define a complete SEooC with automatic documentation generation. +Builds Sphinx-based HTML documentation from RST source files with support for dependencies and cross-referencing. .. code-block:: python - dependable_element( - name = "my_seooc", - description = "My safety-critical component", - assumptions_of_use = [":aous"], - requirements = [":requirements"], - architectural_design = [":architecture"], - dependability_analysis = [":analysis"], - components = [":my_component"], - tests = ["//tests:system_test"], - deps = ["@platform//:platform_module"], - ) - -**Generated Targets:** - -- ````: Sphinx module with HTML documentation -- ``_needs``: Sphinx-needs JSON for cross-referencing -- ``_index``: Generated index.rst with artifact structure - + sphinx_module( + name = "my_docs", srcs = glob(["docs/**/*.rst"]), index = "docs/index.rst", deps = ["@external_module//:docs"], + testonly = False, ) **Key Parameters:** - ``srcs``: RST/MD source files -- ``index``: Main index.rst file +- ``index``: Main index.rst file (mandatory) - ``deps``: Other sphinx_module or dependable_element targets for cross-referencing - ``sphinx``: Sphinx build binary (default: ``//bazel/rules/rules_score:score_build``) +- ``testonly``: If true, only testonly targets can depend on this (default: False) +- ``visibility``: Bazel visibility (default: ``["//visibility:public"]``) **Output:** ``/html/`` with merged dependency documentation +**Note:** Configuration file (``conf.py``) is automatically generated from a template. + + +Dependency Management +--------------------- + +Use ``deps`` for cross-module references. HTML is automatically merged: + +.. code-block:: text + + /html/ + ├── index.html # Main documentation + ├── _static/ + ├── dependency1/ # Merged dependency + └── dependency2/ + Artifact Rules -------------- @@ -215,7 +169,6 @@ Artifact rules define S-CORE process work products. All provide ``SphinxSourcesI component_requirements( name = "requirements", srcs = ["docs/requirements.rst"], - feature_requirement = [":features"], ) **assumptions_of_use** @@ -225,6 +178,8 @@ Artifact rules define S-CORE process work products. All provide ``SphinxSourcesI assumptions_of_use( name = "aous", srcs = ["docs/assumptions.rst"], + feature_requirement = [":features"], + component_requirements = [":requirements"], ) **architectural_design** @@ -257,6 +212,7 @@ Artifact rules define S-CORE process work products. All provide ``SphinxSourcesI name = "analysis", arch_design = ":architecture", dfa = ["docs/dfa.rst"], + fmea = ["docs/fmea.rst"], safety_analysis = [":safety"], ) @@ -275,8 +231,19 @@ Define the smallest testable software element. unit_design = [":architecture"], implementation = ["//src:lib"], tests = ["//tests:unit_test"], + scope = [], + testonly = True, ) +**Parameters:** + +- ``unit_design``: List of architectural_design targets describing the unit (mandatory) +- ``implementation``: List of implementation targets like cc_library, py_library, etc. (mandatory) +- ``tests``: List of test targets that verify the unit (mandatory) +- ``scope``: Additional targets needed for unit implementation (default: []) +- ``testonly``: If true, only testonly targets can depend on this unit (default: True) +- ``visibility``: Bazel visibility specification + **component** Define a collection of units. @@ -285,12 +252,20 @@ Define a collection of units. component( name = "my_component", - component_requirements = [":requirements"], - units = [":my_unit"], - implementation = ["//src:binary"], + requirements = [":requirements"], + components = [":my_unit"], tests = ["//tests:integration_test"], + testonly = True, ) +**Parameters:** + +- ``requirements``: List of component_requirements targets (mandatory) +- ``components``: List of unit or component targets that comprise this component (mandatory) +- ``tests``: List of component-level integration test targets (mandatory) +- ``testonly``: If true, only testonly targets can depend on this component (default: True) +- ``visibility``: Bazel visibility specification + **dependable_element** Define a complete SEooC with automatic documentation generation. @@ -306,9 +281,25 @@ Define a complete SEooC with automatic documentation generation. dependability_analysis = [":analysis"], components = [":my_component"], tests = ["//tests:system_test"], + checklists = [], deps = ["@platform//:platform_module"], + testonly = True, ) +**Parameters:** + +- ``description``: High-level description of the element (supports RST formatting) +- ``assumptions_of_use``: List of assumptions_of_use targets (mandatory) +- ``requirements``: List of requirements targets (component_requirements, feature_requirements, etc.) (mandatory) +- ``architectural_design``: List of architectural_design targets (mandatory) +- ``dependability_analysis``: List of dependability_analysis targets (mandatory) +- ``components``: List of component and/or unit targets (mandatory) +- ``tests``: List of system-level test targets (mandatory) +- ``checklists``: Optional list of safety checklist files (default: []) +- ``deps``: Optional list of other module targets for cross-referencing (default: []) +- ``testonly``: If true, only testonly targets can depend on this (default: True) +- ``visibility``: Bazel visibility specification + **Generated Targets:** - ````: Sphinx module with HTML documentation @@ -324,26 +315,13 @@ The macro automatically: - Delegates to ``sphinx_module`` for actual Sphinx build and HTML generation - Integrates dependencies for cross-module referencing and HTML merging -Dependency Management ---------------------- - -Use ``deps`` for cross-module references. HTML is automatically merged: - -.. code-block:: text - - /html/ - ├── index.html # Main documentation - ├── _static/ - ├── dependency1/ # Merged dependency - └── dependency2/ - Complete Example ---------------- .. code-block:: python - load("//bazel/rules/rules_score:rules_score.bzl", + load("@score_tooling//bazel/rules/rules_score:rules_score.bzl", "architectural_design", "assumptions_of_use", "component", "component_requirements", "dependability_analysis", "dependable_element", @@ -351,14 +329,18 @@ Complete Example # Artifacts feature_requirements(name = "features", srcs = ["docs/features.rst"]) - component_requirements(name = "reqs", srcs = ["docs/reqs.rst"], - feature_requirement = [":features"]) - assumptions_of_use(name = "aous", srcs = ["docs/aous.rst"]) + component_requirements(name = "reqs", srcs = ["docs/reqs.rst"]) + assumptions_of_use(name = "aous", srcs = ["docs/aous.rst"], + feature_requirement = [":features"]) architectural_design(name = "arch", static = ["docs/arch.rst"], dynamic = ["docs/dynamic.rst"]) - safety_analysis(name = "safety", arch_design = ":arch") + safety_analysis(name = "safety", arch_design = ":arch", + controlmeasures = ["docs/controls.rst"], + failuremodes = ["docs/failures.rst"], + fta = ["docs/fta.rst"]) dependability_analysis(name = "analysis", arch_design = ":arch", dfa = ["docs/dfa.rst"], + fmea = ["docs/fmea.rst"], safety_analysis = [":safety"]) # Implementation @@ -369,7 +351,7 @@ Complete Example unit(name = "kvs_unit", unit_design = [":arch"], implementation = [":kvs_lib"], tests = [":kvs_test"]) component(name = "kvs_component", requirements = [":reqs"], - units = [":kvs_unit"], implementation = [":kvs_lib"], tests = []) + components = [":kvs_unit"], tests = []) # SEooC dependable_element( @@ -390,46 +372,16 @@ Build: bazel build //:persistency_kvs # Output: bazel-bin/persistency_kvs/html/ - - # Implementation - cc_library(name = "kvs_lib", srcs = ["kvs.cpp"], hdrs = ["kvs.h"]) - cc_test(name = "kvs_test", srcs = ["kvs_test.cpp"], deps = [":kvs_lib"]) - - # Structure - unit(name = "kvs_unit", unit_design = [":arch"], - implementation = [":kvs_lib"], tests = [":kvs_test"]) - component(name = "kvs_component", component_requirements = [":reqs"], - units = [":kvs_unit"], implementation = [":kvs_lib"], tests = []) - - # SEooC - dependable_element( - name = "persistency_kvs", - description = "Key-Value Store for persistent data storage", - assumptions_of_use = [":aous"], - requirements = [":reqs"], - architectural_design = [":arch"], - dependability_analysis = [":analysis"], - components = [":kvs_component"], - tests = [], - deps = ["@score_process//:score_process_module"], - ) - -Build: - -.. code-block:: bash - - bazel build //:kvs_seooc - # Output: bazel-bin/kvs_seooc/html/ - # Includes merged HTML from score_platform and score_process modules + # Includes merged HTML from dependencies like score_process Design Rationale ---------------- These rules provide a structured approach to documentation by: -1. **Two-Tier Architecture**: Generic ``sphinx_module`` for flexibility, specialized ``score_component`` for safety-critical work +1. **Two-Tier Architecture**: Generic ``sphinx_module`` for flexibility, specialized artifact rules for safety-critical work 2. **Dependency Management**: Automatic cross-referencing and HTML merging across modules -3. **Standardization**: SEooC enforces consistent structure for safety documentation +3. **Standardization**: ``dependable_element`` enforces consistent structure for safety documentation 4. **Traceability**: Sphinx-needs integration enables bidirectional traceability 5. **Automation**: Index generation, symlinking, and configuration management are automatic 6. **Build System Integration**: Bazel ensures reproducible, cacheable documentation builds diff --git a/bazel/rules/rules_score/private/dependable_element.bzl b/bazel/rules/rules_score/private/dependable_element.bzl index da368f9..c90df37 100644 --- a/bazel/rules/rules_score/private/dependable_element.bzl +++ b/bazel/rules/rules_score/private/dependable_element.bzl @@ -713,7 +713,6 @@ def dependable_element( tests, checklists = [], deps = [], - sphinx = Label("@score_tooling//bazel/rules/rules_score:score_build"), testonly = True, visibility = None): """Define a dependable element (Safety Element out of Context - SEooC) following S-CORE process guidelines. @@ -783,7 +782,6 @@ def dependable_element( srcs = [":" + name + "_index"], index = ":" + name + "_index", deps = deps, - sphinx = sphinx, testonly = testonly, visibility = visibility, ) diff --git a/bazel/rules/rules_score/private/sphinx_module.bzl b/bazel/rules/rules_score/private/sphinx_module.bzl index 2ab3d29..c429b27 100644 --- a/bazel/rules/rules_score/private/sphinx_module.bzl +++ b/bazel/rules/rules_score/private/sphinx_module.bzl @@ -26,20 +26,18 @@ def _create_config_py(ctx): Args: ctx: Rule context """ - if ctx.attr.config: - config_file = ctx.attr.config.files.to_list()[0] - else: - config_file = ctx.actions.declare_file(ctx.label.name + "/conf.py") - template = ctx.file._config_template - - # Read template and substitute PROJECT_NAME - ctx.actions.expand_template( - template = template, - output = config_file, - substitutions = { - "{PROJECT_NAME}": ctx.label.name.replace("_", " ").title(), - }, - ) + sphinx_toolchain = ctx.toolchains["//bazel/rules/rules_score:toolchain_type"].sphinxinfo + config_file = ctx.actions.declare_file(ctx.label.name + "/conf.py") + template = sphinx_toolchain.conf_template.files.to_list()[0] + + # Read template and substitute PROJECT_NAME + ctx.actions.expand_template( + template = template, + output = config_file, + substitutions = { + "{PROJECT_NAME}": ctx.label.name.replace("_", " ").title(), + }, + ) return config_file # ====================================================================================== @@ -50,17 +48,6 @@ sphinx_rule_attrs = { allow_files = True, doc = "List of source files for the Sphinx documentation.", ), - "sphinx": attr.label( - doc = "The Sphinx build binary to use.", - mandatory = True, - executable = True, - cfg = "exec", - ), - "config": attr.label( - allow_files = [".py"], - doc = "Configuration file (conf.py) for the Sphinx documentation. If not provided, a default config will be generated.", - mandatory = False, - ), "index": attr.label( allow_files = [".rst"], doc = "Index file (index.rst) for the Sphinx documentation.", @@ -69,23 +56,13 @@ sphinx_rule_attrs = { "deps": attr.label_list( doc = "List of other sphinx_module targets this module depends on for intersphinx.", ), - "_config_template": attr.label( - default = Label("//bazel/rules/rules_score:templates/conf.template.py"), - allow_single_file = True, - doc = "Template for generating default conf.py", - ), - "_html_merge_tool": attr.label( - default = Label("//bazel/rules/rules_score:sphinx_html_merge"), - executable = True, - cfg = "exec", - doc = "Tool for merging HTML directories", - ), } # ====================================================================================== # Rule implementations # ====================================================================================== def _score_needs_impl(ctx): + sphinx_toolchain = ctx.toolchains["//bazel/rules/rules_score:toolchain_type"].sphinxinfo output_path = ctx.label.name.replace("_needs", "") + "/needs.json" needs_output = ctx.actions.declare_file(output_path) @@ -95,9 +72,6 @@ def _score_needs_impl(ctx): # Phase 1: Build needs.json (without external needs) needs_inputs = ctx.files.srcs + [config_file] - if ctx.attr.config: - needs_inputs = needs_inputs + ctx.files.config - needs_args = [ "--index_file", ctx.attr.index.files.to_list()[0].path, @@ -114,7 +88,10 @@ def _score_needs_impl(ctx): outputs = [needs_output], arguments = needs_args, progress_message = "Generating needs.json for: %s" % ctx.label.name, - executable = ctx.executable.sphinx, + executable = sphinx_toolchain.sphinx.files_to_run.executable, + tools = [ + sphinx_toolchain.sphinx.files_to_run, + ], ) transitive_needs = [dep[SphinxNeedsInfo].needs_json_files for dep in ctx.attr.deps if SphinxNeedsInfo in dep] @@ -139,7 +116,7 @@ def _score_html_impl(ctx): # Collect all transitive dependencies with deduplication modules = [] - + sphinx_toolchain = ctx.toolchains["//bazel/rules/rules_score:toolchain_type"].sphinxinfo needs_external_needs = {} for dep in ctx.attr.needs: if SphinxNeedsInfo in dep: @@ -164,7 +141,7 @@ def _score_html_impl(ctx): # Read template and substitute PROJECT_NAME config_file = ctx.actions.declare_file(ctx.label.name + "/conf.py") - template = ctx.file._config_template + template = sphinx_toolchain.conf_template.files.to_list()[0] ctx.actions.expand_template( template = template, @@ -193,7 +170,10 @@ def _score_html_impl(ctx): outputs = [sphinx_html_output], arguments = html_args, progress_message = "Building HTML: %s" % ctx.label.name, - executable = ctx.executable.sphinx, + executable = sphinx_toolchain.sphinx.files_to_run.executable, + tools = [ + sphinx_toolchain.sphinx.files_to_run, + ], ) # Create final HTML output directory with dependencies using Python merge script @@ -223,7 +203,8 @@ def _score_html_impl(ctx): outputs = [html_output], arguments = merge_args, progress_message = "Merging HTML with dependencies for %s" % ctx.label.name, - executable = ctx.executable._html_merge_tool, + executable = sphinx_toolchain.html_merge_tool.files_to_run.executable, + tools = [sphinx_toolchain.html_merge_tool.files_to_run], ) return [ @@ -240,6 +221,7 @@ def _score_html_impl(ctx): _score_needs = rule( implementation = _score_needs_impl, attrs = sphinx_rule_attrs, + toolchains = ["//bazel/rules/rules_score:toolchain_type"], ) _score_html = rule( @@ -248,6 +230,7 @@ _score_html = rule( allow_files = True, doc = "Submodule symbols.needs targets for this module.", )), + toolchains = ["//bazel/rules/rules_score:toolchain_type"], ) # ====================================================================================== @@ -258,7 +241,6 @@ def sphinx_module( name, srcs, index, - config = None, deps = [], sphinx = Label("//bazel/rules/rules_score:score_build"), testonly = False, @@ -281,10 +263,8 @@ def sphinx_module( _score_needs( name = name + "_needs", srcs = srcs, - config = config, index = index, deps = [d + "_needs" for d in deps], - sphinx = sphinx, testonly = testonly, visibility = visibility, ) @@ -292,11 +272,9 @@ def sphinx_module( _score_html( name = name, srcs = srcs, - config = config, index = index, deps = deps, needs = [d + "_needs" for d in deps], - sphinx = sphinx, testonly = testonly, visibility = visibility, ) diff --git a/bazel/rules/rules_score/rules_score.bzl b/bazel/rules/rules_score/rules_score.bzl index 7084744..513315f 100644 --- a/bazel/rules/rules_score/rules_score.bzl +++ b/bazel/rules/rules_score/rules_score.bzl @@ -1,3 +1,16 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + load("@rules_python//sphinxdocs:sphinx.bzl", "sphinx_docs") load("@rules_python//sphinxdocs:sphinx_docs_library.bzl", "sphinx_docs_library") load( diff --git a/bazel/rules/rules_score/sphinx_toolchain.bzl b/bazel/rules/rules_score/sphinx_toolchain.bzl new file mode 100644 index 0000000..b174723 --- /dev/null +++ b/bazel/rules/rules_score/sphinx_toolchain.bzl @@ -0,0 +1,47 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +SphinxInfo = provider( + doc = "Provider for Sphinx Toolchain", + fields = { + "sphinx": "sphinx executable", + "conf_template": "template for conf.py", + "html_merge_tool": "tool to merge html files", + }, +) + +def _sphinx_toolchain_impl(ctx): + toolchain_info = platform_common.ToolchainInfo( + sphinxinfo = SphinxInfo( + sphinx = ctx.attr.sphinx, + conf_template = ctx.attr.conf_template, + html_merge_tool = ctx.attr.html_merge_tool, + ), + ) + return [toolchain_info] + +sphinx_toolchain = rule( + implementation = _sphinx_toolchain_impl, + attrs = { + "sphinx": attr.label( + default = "@score_tooling//bazel/rules/rules_score:raw_build", + ), + "conf_template": attr.label( + allow_single_file = True, + default = "@score_tooling//bazel/rules/rules_score:templates/conf.template.py", + ), + "html_merge_tool": attr.label( + default = "@score_tooling//bazel/rules/rules_score:sphinx_html_merge", + ), + }, +) diff --git a/bazel/rules/rules_score/src/sphinx_module_ext.py b/bazel/rules/rules_score/src/sphinx_module_ext.py new file mode 100644 index 0000000..7ace006 --- /dev/null +++ b/bazel/rules/rules_score/src/sphinx_module_ext.py @@ -0,0 +1,136 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +# +import json +import logging +import os +import sys +from pathlib import Path +from typing import Any, Dict, List + +# Create a logger with the Sphinx namespace +logger = logging.getLogger(__name__) + +# Configuration constants defined by bazel +NEEDS_EXTERNAL_FILE = "needs_external_needs.json" +BAZEL_OUT_DIR = "bazel-out" + + +def find_workspace_root() -> Path: + """ + Find the Bazel workspace root by looking for the bazel-out directory. + + Returns: + Path to the workspace root directory + """ + current = Path.cwd() + + # Traverse up the directory tree looking for bazel-out + while current != current.parent: + if (current / BAZEL_OUT_DIR).exists(): + return current + current = current.parent + + # If we reach the root without finding it, return current directory + return Path.cwd() + + +def load_external_needs() -> List[Dict[str, Any]]: + """ + Load external needs configuration from JSON file. + + This function reads the needs_external_needs.json file if it exists and + resolves relative paths to absolute paths based on the workspace root. + + Returns: + List of external needs configurations with resolved paths + """ + needs_file = Path(NEEDS_EXTERNAL_FILE) + + if not needs_file.exists(): + logger.info(f"{NEEDS_EXTERNAL_FILE} not found - no external dependencies") + return [] + + logger.info(f"Loading external needs from {NEEDS_EXTERNAL_FILE}") + + try: + with needs_file.open("r", encoding="utf-8") as file: + needs_dict = json.load(file) + except json.JSONDecodeError as e: + logger.error(f"Failed to parse {NEEDS_EXTERNAL_FILE}: {e}") + return [] + except Exception as e: + logger.error(f"Failed to read {NEEDS_EXTERNAL_FILE}: {e}") + return [] + + workspace_root = find_workspace_root() + logger.info(f"Workspace root: {workspace_root}") + + external_needs = [] + for key, config in needs_dict.items(): + if "json_path" not in config: + logger.warning( + f"External needs config for '{key}' missing 'json_path', skipping" + ) + continue + + if "version" not in config: + logger.warning( + f"External needs config for '{key}' missing 'version', skipping" + ) + continue + # Resolve relative path to absolute path + # Bazel provides relative paths like: bazel-out/k8-fastbuild/bin/.../needs.json + # We need absolute paths: .../execroot/_main/bazel-out/... + json_path = workspace_root / config["json_path"] + config["json_path"] = str(json_path) + + logger.info(f"Added external needs config for '{key}':") + logger.info(f" json_path: {config['json_path']}") + logger.info(f" id_prefix: {config.get('id_prefix', 'none')}") + logger.info(f" version: {config['version']}") + + external_needs.append(config) + + return external_needs + + +def init_external_needs(app: Any, config: Any) -> None: + """ + Initialize external needs configuration. + + Args: + app: Sphinx application object + config: Sphinx configuration object + """ + + config.needs_external_needs = load_external_needs() + + +def setup(app: Any) -> Dict[str, Any]: + """ + Sphinx setup hook to register event listeners. + + Args: + app: Sphinx application object + + Returns: + Extension metadata dictionary + """ + app.connect("config-inited", init_external_needs) + + return { + "version": "1.0", + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/bazel/rules/rules_score/templates/conf.template.py b/bazel/rules/rules_score/templates/conf.template.py index e916952..923fb87 100644 --- a/bazel/rules/rules_score/templates/conf.template.py +++ b/bazel/rules/rules_score/templates/conf.template.py @@ -22,8 +22,8 @@ import os from pathlib import Path from typing import Any, Dict, List -from sphinx.util import logging +from sphinx.util import logging # Create a logger with the Sphinx namespace logger = logging.getLogger(__name__) @@ -39,15 +39,11 @@ # Sphinx extensions - comprehensive list for SCORE modules extensions = [ + "sphinx_module_ext", "sphinx_needs", "sphinx_design", "myst_parser", "sphinxcontrib.plantuml", - "score_plantuml", - "score_metamodel", - "score_draw_uml_funcs", - "score_source_code_linker", - "score_layout", ] # MyST parser extensions @@ -69,139 +65,4 @@ numfig = True # HTML theme -# html_theme = "pydata_sphinx_theme" - - -# Configuration constants -NEEDS_EXTERNAL_FILE = "needs_external_needs.json" -BAZEL_OUT_DIR = "bazel-out" - - -def find_workspace_root() -> Path: - """ - Find the Bazel workspace root by looking for the bazel-out directory. - - Returns: - Path to the workspace root directory - """ - current = Path.cwd() - - # Traverse up the directory tree looking for bazel-out - while current != current.parent: - if (current / BAZEL_OUT_DIR).exists(): - return current - current = current.parent - - # If we reach the root without finding it, return current directory - return Path.cwd() - - -def load_external_needs() -> List[Dict[str, Any]]: - """ - Load external needs configuration from JSON file. - - This function reads the needs_external_needs.json file if it exists and - resolves relative paths to absolute paths based on the workspace root. - - Returns: - List of external needs configurations with resolved paths - """ - needs_file = Path(NEEDS_EXTERNAL_FILE) - - if not needs_file.exists(): - logger.info(f"{NEEDS_EXTERNAL_FILE} not found - no external dependencies") - return [] - - logger.info(f"Loading external needs from {NEEDS_EXTERNAL_FILE}") - - try: - with needs_file.open("r", encoding="utf-8") as file: - needs_dict = json.load(file) - except json.JSONDecodeError as e: - logger.error(f"Failed to parse {NEEDS_EXTERNAL_FILE}: {e}") - return [] - except Exception as e: - logger.error(f"Failed to read {NEEDS_EXTERNAL_FILE}: {e}") - return [] - - workspace_root = find_workspace_root() - logger.info(f"Workspace root: {workspace_root}") - - external_needs = [] - for key, config in needs_dict.items(): - if "json_path" not in config: - logger.warning( - f"External needs config for '{key}' missing 'json_path', skipping" - ) - continue - - # Resolve relative path to absolute path - # Bazel provides relative paths like: bazel-out/k8-fastbuild/bin/.../needs.json - # We need absolute paths: .../execroot/_main/bazel-out/... - json_path = workspace_root / config["json_path"] - config["json_path"] = str(json_path) - - logger.info(f"Added external needs config for '{key}':") - logger.info(f" json_path: {config['json_path']}") - logger.info(f" id_prefix: {config.get('id_prefix', 'none')}") - logger.info(f" version: {config.get('version', 'none')}") - - external_needs.append(config) - - return external_needs - - -def verify_config(app: Any, config: Any) -> None: - """ - Initialize and verify external needs configuration. - - This is called during Sphinx's config-inited event to ensure - external needs configuration is correctly set up. We need to - explicitly set the config value here because Sphinx doesn't - automatically pick up module-level variables for extension configs. - - Args: - app: Sphinx application object - config: Sphinx configuration object - """ - # Set the config from our module-level variable - # This is needed because sphinx-needs registers its config with add_config_value - # which doesn't automatically pick up module-level variables from conf.py - if needs_external_needs: - config.needs_external_needs = needs_external_needs - - logger.info("=" * 80) - logger.info("Verifying Sphinx configuration") - logger.info(f" Project: {config.project}") - logger.info(f" External needs count: {len(config.needs_external_needs)}") - logger.info("=" * 80) - - -def setup(app: Any) -> Dict[str, Any]: - """ - Sphinx setup hook to register event listeners. - - Args: - app: Sphinx application object - - Returns: - Extension metadata dictionary - """ - app.connect("config-inited", verify_config) - - return { - "version": "1.0", - "parallel_read_safe": True, - "parallel_write_safe": True, - } - - -# Initialize external needs configuration -logger.info("=" * 80) -logger.info(f"Sphinx configuration loaded for project: {project}") -logger.info(f"Current working directory: {Path.cwd()}") - -# Load external needs configuration -# Note: This sets a module-level variable that is then applied to the Sphinx -# config object in the verify_config callback during the config-inited event -needs_external_needs = load_external_needs() +html_theme = "sphinx_rtd_theme" diff --git a/bazel/rules/rules_score/test/.bazelrc b/bazel/rules/rules_score/test/.bazelrc new file mode 100644 index 0000000..3a6ddac --- /dev/null +++ b/bazel/rules/rules_score/test/.bazelrc @@ -0,0 +1,7 @@ +common --registry=https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/ +common --registry=https://bcr.bazel.build + +build --java_language_version=17 +build --tool_java_language_version=17 +build --java_runtime_version=remotejdk_17 +build --tool_java_runtime_version=remotejdk_17 diff --git a/bazel/rules/rules_score/test/.bazelversion b/bazel/rules/rules_score/test/.bazelversion new file mode 100644 index 0000000..56b6be4 --- /dev/null +++ b/bazel/rules/rules_score/test/.bazelversion @@ -0,0 +1 @@ +8.3.1 diff --git a/bazel/rules/rules_score/test/BUILD b/bazel/rules/rules_score/test/BUILD index e1690cd..277f256 100644 --- a/bazel/rules/rules_score/test/BUILD +++ b/bazel/rules/rules_score/test/BUILD @@ -10,8 +10,10 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@aspect_rules_py//py:defs.bzl", "py_binary") +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load( - "//bazel/rules/rules_score:rules_score.bzl", + "@score_tooling//bazel/rules/rules_score:rules_score.bzl", "architectural_design", "assumptions_of_use", "component", @@ -23,6 +25,10 @@ load( "sphinx_module", "unit", ) +load( + "@score_tooling//bazel/rules/rules_score:sphinx_toolchain.bzl", + "sphinx_toolchain", +) load( ":html_generation_test.bzl", "html_merging_test", @@ -58,14 +64,14 @@ sphinx_module( name = "module_c_lib", srcs = glob(["fixtures/module_c/*.rst"]), index = "fixtures/module_c/index.rst", - sphinx = "//bazel/rules/rules_score:score_build", + sphinx = "@score_tooling//bazel/rules/rules_score:score_build", ) sphinx_module( name = "module_b_lib", srcs = glob(["fixtures/module_b/*.rst"]), index = "fixtures/module_b/index.rst", - sphinx = "//bazel/rules/rules_score:score_build", + sphinx = "@score_tooling//bazel/rules/rules_score:score_build", deps = [":module_c_lib"], ) @@ -73,7 +79,7 @@ sphinx_module( name = "module_a_lib", srcs = glob(["fixtures/module_a/*.rst"]), index = "fixtures/module_a/index.rst", - sphinx = "//bazel/rules/rules_score:score_build", + sphinx = "@score_tooling//bazel/rules/rules_score:score_build", deps = [ ":module_b_lib", ":module_c_lib", @@ -387,3 +393,45 @@ test_suite( ":unit_component_tests", ], ) + +# ============================================================================ +# Toolchain for the score build +# ============================================================================ +py_binary( + name = "score_build", + srcs = ["@score_tooling//bazel/rules/rules_score:src/sphinx_wrapper.py"], + data = [], + env = { + "SOURCE_DIRECTORY": "", + "DATA": "", + "ACTION": "check", + }, + main = "@score_tooling//bazel/rules/rules_score:src/sphinx_wrapper.py", + visibility = ["//visibility:public"], + deps = [ + "@score_docs_as_code//src:plantuml_for_python", + "@score_docs_as_code//src/extensions/score_sphinx_bundle", + "@score_tooling//bazel/rules/rules_score:sphinx_module_ext", + ], +) + +sphinx_toolchain( + name = "score_sphinx_toolchain", + conf_template = "template/conf.template.py", + sphinx = ":score_build", +) + +toolchain( + name = "score_toolchain", + exec_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + target_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + toolchain = ":score_sphinx_toolchain", + toolchain_type = "@score_tooling//bazel/rules/rules_score:toolchain_type", + visibility = ["//visibility:public"], +) diff --git a/bazel/rules/rules_score/test/MODULE.bazel b/bazel/rules/rules_score/test/MODULE.bazel new file mode 100644 index 0000000..eae15e6 --- /dev/null +++ b/bazel/rules/rules_score/test/MODULE.bazel @@ -0,0 +1,69 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +module( + name = "rules_score_test", + version = "0.0.0", + compatibility_level = 1, +) + +############################################################################### +# Core Dependencies +############################################################################### +bazel_dep(name = "rules_python", version = "1.4.1") +bazel_dep(name = "rules_cc", version = "0.2.17") +bazel_dep(name = "aspect_rules_py", version = "1.4.0") +bazel_dep(name = "aspect_rules_lint", version = "1.5.3") +bazel_dep(name = "rules_shell", version = "0.5.0") +bazel_dep(name = "bazel_skylib", version = "1.7.1") +bazel_dep(name = "platforms", version = "1.0.0") + +############################################################################### +# Python Toolchain +############################################################################### +PYTHON_VERSION = "3.12" + +python = use_extension("@rules_python//python/extensions:python.bzl", "python") +python.toolchain( + is_default = True, + python_version = PYTHON_VERSION, +) + +############################################################################### +# Pip Hub +############################################################################### +pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") + +# Main tooling pip +pip.parse( + envsubst = ["PIP_INDEX_URL"], + extra_pip_args = ["--index-url=${PIP_INDEX_URL:-https://pypi.org/simple/}"], + hub_name = "pip_tooling_test", + python_version = PYTHON_VERSION, + requirements_lock = "//:requirements.txt", +) +use_repo(pip, "pip_tooling") + +bazel_dep(name = "score_docs_as_code", version = "3.0.1", dev_dependency = True) +# bazel_dep(name = "score_platform", version = "0.5.0") +# bazel_dep(name = "score_process", version = "1.3.2") + +bazel_dep(name = "score_tooling", version = "1.3.2") +local_path_override( + module_name = "score_tooling", + path = "../../../..", +) + +register_toolchains( + "//:score_toolchain", +) diff --git a/bazel/rules/rules_score/test/html_generation_test.bzl b/bazel/rules/rules_score/test/html_generation_test.bzl index 39f06cd..02545c5 100644 --- a/bazel/rules/rules_score/test/html_generation_test.bzl +++ b/bazel/rules/rules_score/test/html_generation_test.bzl @@ -13,7 +13,7 @@ """Test rules for sphinx_module HTML generation and dependencies.""" load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") -load("//bazel/rules/rules_score/private:sphinx_module.bzl", "SphinxModuleInfo", "SphinxNeedsInfo") +load("@score_tooling//bazel/rules/rules_score/private:sphinx_module.bzl", "SphinxModuleInfo", "SphinxNeedsInfo") # ============================================================================ # Provider Tests diff --git a/bazel/rules/rules_score/test/requirements.txt b/bazel/rules/rules_score/test/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/bazel/rules/rules_score/test/seooc_test.bzl b/bazel/rules/rules_score/test/seooc_test.bzl index a88a637..df6be3b 100644 --- a/bazel/rules/rules_score/test/seooc_test.bzl +++ b/bazel/rules/rules_score/test/seooc_test.bzl @@ -9,7 +9,7 @@ Tests the SEooC (Safety Element out of Context) functionality including: """ load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") -load("//bazel/rules/rules_score/private:sphinx_module.bzl", "SphinxModuleInfo", "SphinxNeedsInfo") +load("@score_tooling//bazel/rules/rules_score/private:sphinx_module.bzl", "SphinxModuleInfo", "SphinxNeedsInfo") def _seooc_index_generation_test_impl(ctx): """Test that dependable_element generates proper index.rst file.""" diff --git a/bazel/rules/rules_score/test/template/conf.template.py b/bazel/rules/rules_score/test/template/conf.template.py new file mode 100644 index 0000000..b6dfb54 --- /dev/null +++ b/bazel/rules/rules_score/test/template/conf.template.py @@ -0,0 +1,73 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +""" +Generic Sphinx configuration template for SCORE modules. + +This file is auto-generated from a template and should not be edited directly. +Template variables like {PROJECT_NAME} are replaced during Bazel build. +""" + +import json +import os +from pathlib import Path +from typing import Any, Dict, List + +from sphinx.util import logging + +# Create a logger with the Sphinx namespace +logger = logging.getLogger(__name__) + +# Project configuration - {PROJECT_NAME} will be replaced by the module name during build +project = "{PROJECT_NAME}" +author = "S-CORE" +version = "1.0" +release = "1.0.0" +project_url = ( + "https://github.com/eclipse-score" # Required by score_metamodel extension +) + +# Sphinx extensions - comprehensive list for SCORE modules +extensions = [ + "sphinx_module_ext", + "sphinx_needs", + "sphinx_design", + "myst_parser", + "sphinxcontrib.plantuml", + "score_plantuml", + "score_metamodel", + "score_draw_uml_funcs", + "score_source_code_linker", + "score_layout", +] + +# MyST parser extensions +myst_enable_extensions = ["colon_fence"] + +# Exclude patterns for Bazel builds +exclude_patterns = [ + "bazel-*", + ".venv*", +] + +# Enable markdown rendering +source_suffix = { + ".rst": "restructuredtext", + ".md": "markdown", +} + +# Enable numref for cross-references +numfig = True + +# HTML theme +html_theme = "sphinx_rtd_theme" diff --git a/bazel/rules/rules_score/test/unit_component_test.bzl b/bazel/rules/rules_score/test/unit_component_test.bzl index f62f4b8..7d7b918 100644 --- a/bazel/rules/rules_score/test/unit_component_test.bzl +++ b/bazel/rules/rules_score/test/unit_component_test.bzl @@ -20,7 +20,7 @@ Tests the new hierarchical structure for S-CORE process compliance: """ load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") -load("//bazel/rules/rules_score:providers.bzl", "ComponentInfo", "SphinxSourcesInfo", "UnitInfo") +load("@score_tooling//bazel/rules/rules_score:providers.bzl", "ComponentInfo", "SphinxSourcesInfo", "UnitInfo") # ============================================================================ # Unit Tests diff --git a/python_basics/private/BUILD b/python_basics/private/BUILD index 1a7e78c..b3b1d23 100644 --- a/python_basics/private/BUILD +++ b/python_basics/private/BUILD @@ -54,8 +54,8 @@ format_test( # `bazel run //:requirements.update -- --upgrade`. compile_pip_requirements( name = "requirements", - src = "//:requirements.in", - requirements_txt = "//:requirements.txt", + src = "//python_basics:requirements.in", + requirements_txt = "//python_basics:requirements.txt", tags = [ "manual", ], diff --git a/python_basics/requirements.in b/python_basics/requirements.in index b1fc693..05d941e 100644 --- a/python_basics/requirements.in +++ b/python_basics/requirements.in @@ -1,2 +1,8 @@ pytest basedpyright +sphinx +sphinx-needs +sphinx-design +myst-parser +sphinxcontrib-plantuml +sphinx-rtd-theme diff --git a/python_basics/requirements.txt b/python_basics/requirements.txt index fcfba3f..364a4d3 100644 --- a/python_basics/requirements.txt +++ b/python_basics/requirements.txt @@ -2,16 +2,301 @@ # This file is autogenerated by pip-compile with Python 3.12 # by the following command: # -# bazel run //private:requirements.update +# bazel run //python_basics/private:requirements.update # +alabaster==1.0.0 \ + --hash=sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e \ + --hash=sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b + # via sphinx +babel==2.18.0 \ + --hash=sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d \ + --hash=sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35 + # via sphinx basedpyright==1.35.0 \ --hash=sha256:2a7e0bd476623d48499e2b18ff6ed19dc28c51909cf9e1152ad355b5809049ad \ --hash=sha256:4f4f84023df5a0cd4ee154916ba698596682ac98bacfa22c941ed6aaf07bba4e - # via -r requirements.in + # via -r python_basics/requirements.in +certifi==2026.2.25 \ + --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ + --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 + # via requests +charset-normalizer==3.4.4 \ + --hash=sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad \ + --hash=sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93 \ + --hash=sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394 \ + --hash=sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89 \ + --hash=sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc \ + --hash=sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86 \ + --hash=sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63 \ + --hash=sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d \ + --hash=sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f \ + --hash=sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8 \ + --hash=sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0 \ + --hash=sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505 \ + --hash=sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161 \ + --hash=sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af \ + --hash=sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152 \ + --hash=sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318 \ + --hash=sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72 \ + --hash=sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4 \ + --hash=sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e \ + --hash=sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3 \ + --hash=sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576 \ + --hash=sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c \ + --hash=sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1 \ + --hash=sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8 \ + --hash=sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1 \ + --hash=sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2 \ + --hash=sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44 \ + --hash=sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26 \ + --hash=sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88 \ + --hash=sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016 \ + --hash=sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede \ + --hash=sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf \ + --hash=sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a \ + --hash=sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc \ + --hash=sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0 \ + --hash=sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84 \ + --hash=sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db \ + --hash=sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1 \ + --hash=sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7 \ + --hash=sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed \ + --hash=sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8 \ + --hash=sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133 \ + --hash=sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e \ + --hash=sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef \ + --hash=sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14 \ + --hash=sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2 \ + --hash=sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0 \ + --hash=sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d \ + --hash=sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828 \ + --hash=sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f \ + --hash=sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf \ + --hash=sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6 \ + --hash=sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328 \ + --hash=sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090 \ + --hash=sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa \ + --hash=sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381 \ + --hash=sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c \ + --hash=sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb \ + --hash=sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc \ + --hash=sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a \ + --hash=sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec \ + --hash=sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc \ + --hash=sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac \ + --hash=sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e \ + --hash=sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313 \ + --hash=sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569 \ + --hash=sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3 \ + --hash=sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d \ + --hash=sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525 \ + --hash=sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894 \ + --hash=sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3 \ + --hash=sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9 \ + --hash=sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a \ + --hash=sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9 \ + --hash=sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14 \ + --hash=sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25 \ + --hash=sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50 \ + --hash=sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf \ + --hash=sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1 \ + --hash=sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3 \ + --hash=sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac \ + --hash=sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e \ + --hash=sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815 \ + --hash=sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c \ + --hash=sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6 \ + --hash=sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6 \ + --hash=sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e \ + --hash=sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4 \ + --hash=sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84 \ + --hash=sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69 \ + --hash=sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15 \ + --hash=sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191 \ + --hash=sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0 \ + --hash=sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897 \ + --hash=sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd \ + --hash=sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2 \ + --hash=sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794 \ + --hash=sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d \ + --hash=sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074 \ + --hash=sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3 \ + --hash=sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224 \ + --hash=sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838 \ + --hash=sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a \ + --hash=sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d \ + --hash=sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d \ + --hash=sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f \ + --hash=sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8 \ + --hash=sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490 \ + --hash=sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966 \ + --hash=sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9 \ + --hash=sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3 \ + --hash=sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e \ + --hash=sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608 + # via requests +docutils==0.22.4 \ + --hash=sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968 \ + --hash=sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de + # via + # myst-parser + # sphinx + # sphinx-rtd-theme +idna==3.11 \ + --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ + --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 + # via requests +imagesize==1.5.0 \ + --hash=sha256:32677681b3f434c2cb496f00e89c5a291247b35b1f527589909e008057da5899 \ + --hash=sha256:8bfc5363a7f2133a89f0098451e0bcb1cd71aba4dc02bbcecb39d99d40e1b94f + # via sphinx iniconfig==2.3.0 \ --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \ --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 # via pytest +jinja2==3.1.6 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 + # via + # myst-parser + # sphinx +jsonschema-rs==0.37.4 \ + --hash=sha256:03b34f911e99343fc388651688683010daee538a3cf8cf86a7997bca28fdf16b \ + --hash=sha256:0f17a61deb557faa57dffb9596e4f022873404f935114367788b1eebdec2bb00 \ + --hash=sha256:10fd978a145a6f8d11373879e7d0ff232b409f77c7faf608e6b4549a7f90aaed \ + --hash=sha256:1d3f8c8b376966c19fd4183fa979dbadc9fdd6070f2bfa4d127bdf70946963cc \ + --hash=sha256:393ece7037a0d19fd528f7a67a32749453876468871a0bd2267909a57d8d4e32 \ + --hash=sha256:5975e448092e99d6cc60793a71f0fee516dbf0fd1e6d2f6f1e4689627268f344 \ + --hash=sha256:67f36f1c445c70f9975d17a84ce37f79593f6234d7eb292830d7749e5fa58ff4 \ + --hash=sha256:75f3b4e0707dcb3dccf911ff49e387b4db54957fe1a19d3423015a65e3762057 \ + --hash=sha256:a56d154b638deb947dbd0dfc285c349eb23a877221f2b0496a2dfa25948cc239 \ + --hash=sha256:dedf72e5e673e3af5b9925979fd71484debada61fb7a3dfabf9bbc74b8012664 \ + --hash=sha256:e159075b1846718466998d5a9294c661113b347b8b4749767680a97c8ed2bf4d \ + --hash=sha256:e93a8720f6e858d872dc2882e7d3b3243ee76f7aa4d60048272773d44df466e7 + # via sphinx-needs +markdown-it-py==4.0.0 \ + --hash=sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147 \ + --hash=sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3 + # via + # mdit-py-plugins + # myst-parser +markupsafe==3.0.3 \ + --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \ + --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \ + --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \ + --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \ + --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \ + --hash=sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c \ + --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \ + --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \ + --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \ + --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \ + --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \ + --hash=sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26 \ + --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \ + --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \ + --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \ + --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \ + --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \ + --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \ + --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \ + --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \ + --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \ + --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \ + --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \ + --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \ + --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \ + --hash=sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758 \ + --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \ + --hash=sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8 \ + --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \ + --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \ + --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \ + --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \ + --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \ + --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \ + --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \ + --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \ + --hash=sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2 \ + --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \ + --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \ + --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \ + --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ + --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \ + --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \ + --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \ + --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \ + --hash=sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e \ + --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \ + --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \ + --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \ + --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \ + --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \ + --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \ + --hash=sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b \ + --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \ + --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \ + --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \ + --hash=sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d \ + --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \ + --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \ + --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \ + --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \ + --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \ + --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \ + --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \ + --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \ + --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \ + --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \ + --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \ + --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \ + --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \ + --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \ + --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \ + --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \ + --hash=sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7 \ + --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \ + --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \ + --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \ + --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \ + --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \ + --hash=sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42 \ + --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \ + --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \ + --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \ + --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \ + --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \ + --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \ + --hash=sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc \ + --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \ + --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 + # via jinja2 +mdit-py-plugins==0.5.0 \ + --hash=sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f \ + --hash=sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6 + # via myst-parser +mdurl==0.1.2 \ + --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \ + --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba + # via markdown-it-py +minijinja==2.16.0 \ + --hash=sha256:01a6144a204019f9cdacec23ccc63dfe429b8f0498454ec213e6a2dd3b329a9b \ + --hash=sha256:13ec22d73f9ebc957b436a33d653df804df8eb7bd04bc7f315275cb296757893 \ + --hash=sha256:413357af0a5cb469ee3c5ebdecb38a84b943c97b09edcbead7c9f1f5f071d7e7 \ + --hash=sha256:4a8948dc9030958fa9bcace34ccdb9457cc025514d5d8058b0d55e3cd8424eb5 \ + --hash=sha256:52bfaca71ad5ddffc412375a7862213348601ea9c9579044244707909622bbd0 \ + --hash=sha256:6109593eeb36e3139d4a7a923e3e45d73143f1bf5838d39731eb883310a2a900 \ + --hash=sha256:6ecc90fcb764c1977eee30ab4d86a672e0d771daf0711bf6cf1c12bac4148d93 \ + --hash=sha256:72e7255e19beac6bb0caa8e3e7669f70709f61398e9d2d0d8c2fb83a279156f6 \ + --hash=sha256:90679250861f33f1d3a18ef29273ed532f18f8b40969c3f805813e9e0f117fc9 \ + --hash=sha256:912784ebbb486bc51539c946e8fa074ff5591e6a14c755f964c96565b1841118 \ + --hash=sha256:cbf0c847f2a2ab8d50d3ceca5fe8b8abe27222c8b5ea30907eb42aabc7f76a4a \ + --hash=sha256:fcb5cabcf6479dc5b51c1efa7ad1b60b82fe96fa0b7e05110967f514b5790ec9 + # via sphinx-needs +myst-parser==5.0.0 \ + --hash=sha256:ab31e516024918296e169139072b81592336f2fef55b8986aa31c9f04b5f7211 \ + --hash=sha256:f6f231452c56e8baa662cc352c548158f6a16fcbd6e3800fc594978002b94f3a + # via -r python_basics/requirements.in nodejs-wheel-binaries==24.11.1 \ --hash=sha256:0e14874c3579def458245cdbc3239e37610702b0aa0975c1dc55e2cb80e42102 \ --hash=sha256:10197b1c9c04d79403501766f76508b0dac101ab34371ef8a46fcf51773497d0 \ @@ -26,7 +311,9 @@ nodejs-wheel-binaries==24.11.1 \ packaging==25.0 \ --hash=sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484 \ --hash=sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f - # via pytest + # via + # pytest + # sphinx pluggy==1.6.0 \ --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 @@ -34,8 +321,173 @@ pluggy==1.6.0 \ pygments==2.19.2 \ --hash=sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887 \ --hash=sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b - # via pytest + # via + # pytest + # sphinx pytest==9.0.1 \ --hash=sha256:3e9c069ea73583e255c3b21cf46b8d3c56f6e3a1a8f6da94ccb0fcf57b9d73c8 \ --hash=sha256:67be0030d194df2dfa7b556f2e56fb3c3315bd5c8822c6951162b92b32ce7dad - # via -r requirements.in + # via -r python_basics/requirements.in +pyyaml==6.0.3 \ + --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ + --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ + --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ + --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \ + --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ + --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \ + --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ + --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \ + --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \ + --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \ + --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ + --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \ + --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \ + --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \ + --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \ + --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ + --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \ + --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \ + --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \ + --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ + --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \ + --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \ + --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ + --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ + --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ + --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ + --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ + --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \ + --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \ + --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \ + --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ + --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ + --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \ + --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ + --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \ + --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \ + --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ + --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ + --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ + --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ + --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ + --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \ + --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \ + --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ + --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ + --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ + --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ + --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ + --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ + --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \ + --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \ + --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \ + --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ + --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ + --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \ + --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ + --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \ + --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \ + --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ + --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \ + --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ + --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \ + --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \ + --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ + --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ + --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ + --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ + --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ + --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ + --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \ + --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ + --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ + --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 + # via myst-parser +requests==2.32.5 \ + --hash=sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6 \ + --hash=sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf + # via + # requests-file + # sphinx + # sphinx-needs +requests-file==2.1.0 \ + --hash=sha256:0f549a3f3b0699415ac04d167e9cb39bccfb730cb832b4d20be3d9867356e658 \ + --hash=sha256:cf270de5a4c5874e84599fc5778303d496c10ae5e870bfa378818f35d21bda5c + # via sphinx-needs +roman-numerals==4.1.0 \ + --hash=sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2 \ + --hash=sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7 + # via sphinx +snowballstemmer==3.0.1 \ + --hash=sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064 \ + --hash=sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895 + # via sphinx +sphinx==9.1.0 \ + --hash=sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb \ + --hash=sha256:c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978 + # via + # -r python_basics/requirements.in + # myst-parser + # sphinx-data-viewer + # sphinx-design + # sphinx-needs + # sphinx-rtd-theme + # sphinxcontrib-jquery + # sphinxcontrib-plantuml +sphinx-data-viewer==0.1.5 \ + --hash=sha256:a7d5e58613562bb745380bfe61ca8b69997998167fd6fa9aea55606c9a4b17e4 \ + --hash=sha256:b74b1d304c505c464d07c7b225ed0d84ea02dcc88bc1c49cdad7c2275fbbdad4 + # via sphinx-needs +sphinx-design==0.7.0 \ + --hash=sha256:d2a3f5b19c24b916adb52f97c5f00efab4009ca337812001109084a740ec9b7a \ + --hash=sha256:f82bf179951d58f55dca78ab3706aeafa496b741a91b1911d371441127d64282 + # via -r python_basics/requirements.in +sphinx-needs==7.0.0 \ + --hash=sha256:3fa665181c323d0b76f6de0d8565f925f36933e8af4c253d34127755eff07371 \ + --hash=sha256:7ea120a1127cc532ea4610e814c26824b05d296ce8c2f6d046d5f6ed64d85d0f + # via -r python_basics/requirements.in +sphinx-rtd-theme==3.1.0 \ + --hash=sha256:1785824ae8e6632060490f67cf3a72d404a85d2d9fc26bce3619944de5682b89 \ + --hash=sha256:b44276f2c276e909239a4f6c955aa667aaafeb78597923b1c60babc76db78e4c + # via -r python_basics/requirements.in +sphinxcontrib-applehelp==2.0.0 \ + --hash=sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1 \ + --hash=sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5 + # via sphinx +sphinxcontrib-devhelp==2.0.0 \ + --hash=sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad \ + --hash=sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2 + # via sphinx +sphinxcontrib-htmlhelp==2.1.0 \ + --hash=sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8 \ + --hash=sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9 + # via sphinx +sphinxcontrib-jquery==4.1 \ + --hash=sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a \ + --hash=sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae + # via + # sphinx-needs + # sphinx-rtd-theme +sphinxcontrib-jsmath==1.0.1 \ + --hash=sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178 \ + --hash=sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8 + # via sphinx +sphinxcontrib-plantuml==0.31 \ + --hash=sha256:fd74752f8ea070e641c3f8a402fccfa1d4a4056e0967b56033d2a76282d9f956 + # via -r python_basics/requirements.in +sphinxcontrib-qthelp==2.0.0 \ + --hash=sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab \ + --hash=sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb + # via sphinx +sphinxcontrib-serializinghtml==2.0.0 \ + --hash=sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331 \ + --hash=sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d + # via sphinx +typing-extensions==4.15.0 \ + --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ + --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 + # via sphinx-needs +urllib3==2.6.3 \ + --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \ + --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4 + # via requests