easylink 0.1.23__tar.gz → 0.1.24__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 (281) hide show
  1. {easylink-0.1.23 → easylink-0.1.24}/CHANGELOG.rst +4 -0
  2. {easylink-0.1.23 → easylink-0.1.24}/PKG-INFO +1 -1
  3. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/pipeline_demo_improved.yaml +3 -3
  4. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/pipeline_demo_naive.yaml +3 -3
  5. easylink-0.1.24/src/easylink/_version.py +1 -0
  6. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/cli.py +1 -0
  7. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/rule.py +2 -1
  8. {easylink-0.1.23 → easylink-0.1.24}/src/easylink.egg-info/PKG-INFO +1 -1
  9. {easylink-0.1.23 → easylink-0.1.24}/src/easylink.egg-info/SOURCES.txt +1 -1
  10. easylink-0.1.23/tests/e2e/test_easylink_run.py → easylink-0.1.24/tests/e2e/test_easylink_cli.py +23 -0
  11. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/e2e/pipeline_cascade.yaml +6 -6
  12. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/e2e/pipeline_splink_dummy.yaml +3 -3
  13. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/e2e/pipeline_with_fastLink.yaml +2 -2
  14. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/test_rule.py +98 -0
  15. easylink-0.1.23/src/easylink/_version.py +0 -1
  16. {easylink-0.1.23 → easylink-0.1.24}/.bandit +0 -0
  17. {easylink-0.1.23 → easylink-0.1.24}/.flake8 +0 -0
  18. {easylink-0.1.23 → easylink-0.1.24}/.github/CODEOWNERS +0 -0
  19. {easylink-0.1.23 → easylink-0.1.24}/.github/pull_request_template.md +0 -0
  20. {easylink-0.1.23 → easylink-0.1.24}/.github/workflows/deploy.yml +0 -0
  21. {easylink-0.1.23 → easylink-0.1.24}/.github/workflows/update_readme.yml +0 -0
  22. {easylink-0.1.23 → easylink-0.1.24}/.gitignore +0 -0
  23. {easylink-0.1.23 → easylink-0.1.24}/.readthedocs.yml +0 -0
  24. {easylink-0.1.23 → easylink-0.1.24}/CONTRIBUTING.rst +0 -0
  25. {easylink-0.1.23 → easylink-0.1.24}/Jenkinsfile +0 -0
  26. {easylink-0.1.23 → easylink-0.1.24}/LICENSE +0 -0
  27. {easylink-0.1.23 → easylink-0.1.24}/Makefile +0 -0
  28. {easylink-0.1.23 → easylink-0.1.24}/README.rst +0 -0
  29. {easylink-0.1.23 → easylink-0.1.24}/docs/Makefile +0 -0
  30. {easylink-0.1.23 → easylink-0.1.24}/docs/nitpick-exceptions +0 -0
  31. {easylink-0.1.23 → easylink-0.1.24}/docs/source/_static/style.css +0 -0
  32. {easylink-0.1.23 → easylink-0.1.24}/docs/source/_templates/layout.html +0 -0
  33. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/cli.rst +0 -0
  34. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/configuration.rst +0 -0
  35. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/graph_components.rst +0 -0
  36. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/implementation.rst +0 -0
  37. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/index.rst +0 -0
  38. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/pipeline.rst +0 -0
  39. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/pipeline_graph.rst +0 -0
  40. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/pipeline_schema.rst +0 -0
  41. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/pipeline_schema_constants/development.rst +0 -0
  42. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/pipeline_schema_constants/index.rst +0 -0
  43. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/pipeline_schema_constants/testing.rst +0 -0
  44. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/rule.rst +0 -0
  45. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/runner.rst +0 -0
  46. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/step.rst +0 -0
  47. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/utilities/aggregator_utils.rst +0 -0
  48. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/utilities/data_utils.rst +0 -0
  49. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/utilities/general_utils.rst +0 -0
  50. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/utilities/index.rst +0 -0
  51. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/utilities/paths.rst +0 -0
  52. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/utilities/splitter_utils.rst +0 -0
  53. {easylink-0.1.23 → easylink-0.1.24}/docs/source/api_reference/utilities/validation_utils.rst +0 -0
  54. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/index.rst +0 -0
  55. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/01_step.drawio.png +0 -0
  56. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/02_default_implementation.drawio.png +0 -0
  57. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/03_slots.drawio.png +0 -0
  58. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/04_data_dependency.drawio.png +0 -0
  59. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/05_pipeline_schema.drawio.png +0 -0
  60. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/06_default_input.drawio.png +0 -0
  61. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/07_cloneable_section.drawio.png +0 -0
  62. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/08_cloneable_section_expanded.drawio.png +0 -0
  63. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/09_loopable_section.drawio.png +0 -0
  64. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/10_loopable_section_expanded.drawio.png +0 -0
  65. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/11_cloneable_section_splitter.drawio.png +0 -0
  66. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/12_cloneable_section_splitter_expanded.drawio.png +0 -0
  67. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/13_autoparallel_section.drawio.png +0 -0
  68. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/14_choice_section.drawio.png +0 -0
  69. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/15_choice_section_expanded.drawio.png +0 -0
  70. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/16_step_hierarchy.drawio.png +0 -0
  71. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/18_schema_to_pipeline.drawio.png +0 -0
  72. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/19_schema_to_pipeline_combined.drawio.png +0 -0
  73. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/clustering_sub_steps.drawio.png +0 -0
  74. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/easylink_pipeline_schema.drawio.png +0 -0
  75. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/entity_resolution_sub_steps.drawio.png +0 -0
  76. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/images/linking_sub_steps.drawio.png +0 -0
  77. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/pipeline_schema/index.rst +0 -0
  78. {easylink-0.1.23 → easylink-0.1.24}/docs/source/concepts/workarounds.rst +0 -0
  79. {easylink-0.1.23 → easylink-0.1.24}/docs/source/conf.py +0 -0
  80. {easylink-0.1.23 → easylink-0.1.24}/docs/source/glossary.rst +0 -0
  81. {easylink-0.1.23 → easylink-0.1.24}/docs/source/index.rst +0 -0
  82. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/cli.rst +0 -0
  83. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/index.rst +0 -0
  84. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/2020/input_file_ssa.parquet +0 -0
  85. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/2020/input_file_w2.parquet +0 -0
  86. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/2030/input_file_ssa.parquet +0 -0
  87. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/2030/input_file_w2.parquet +0 -0
  88. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/DAG-common-pipeline.svg +0 -0
  89. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/DAG-e2e-pipeline-expanded.svg +0 -0
  90. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/DAG-e2e-pipeline.svg +0 -0
  91. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/DAG-r-pyspark.svg +0 -0
  92. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/create_inputs_demo.ipynb +0 -0
  93. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/environment_slurm.yaml +0 -0
  94. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/getting_started.rst +0 -0
  95. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/impl-config-pipeline.yaml +0 -0
  96. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/index.rst +0 -0
  97. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/input_data.yaml +0 -0
  98. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/input_data_demo.yaml +0 -0
  99. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/input_data_demo_2030.yaml +0 -0
  100. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/input_file_1.parquet +0 -0
  101. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/input_file_2.parquet +0 -0
  102. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/input_file_3.parquet +0 -0
  103. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/print_fp_fn_w2_ssa.py +0 -0
  104. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/print_metrics_w2_ssa.py +0 -0
  105. {easylink-0.1.23 → easylink-0.1.24}/docs/source/user_guide/tutorials/r_spark_pipeline.yaml +0 -0
  106. {easylink-0.1.23 → easylink-0.1.24}/pyproject.toml +0 -0
  107. {easylink-0.1.23 → easylink-0.1.24}/python_versions.json +0 -0
  108. {easylink-0.1.23 → easylink-0.1.24}/pytype.cfg +0 -0
  109. {easylink-0.1.23 → easylink-0.1.24}/setup.cfg +0 -0
  110. {easylink-0.1.23 → easylink-0.1.24}/setup.py +0 -0
  111. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/__about__.py +0 -0
  112. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/__init__.py +0 -0
  113. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/configuration.py +0 -0
  114. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/devtools/implementation_creator.py +0 -0
  115. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/graph_components.py +0 -0
  116. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/implementation.py +0 -0
  117. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/implementation_metadata.yaml +0 -0
  118. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/pipeline.py +0 -0
  119. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/pipeline_graph.py +0 -0
  120. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/pipeline_schema.py +0 -0
  121. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/pipeline_schema_constants/__init__.py +0 -0
  122. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/pipeline_schema_constants/development.py +0 -0
  123. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/pipeline_schema_constants/main.py +0 -0
  124. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/pipeline_schema_constants/testing.py +0 -0
  125. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/runner.py +0 -0
  126. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/step.py +0 -0
  127. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/cascading/exclude_clustered.def +0 -0
  128. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/cascading/exclude_clustered.py +0 -0
  129. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/cascading/exclude_none.def +0 -0
  130. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/cascading/exclude_none.py +0 -0
  131. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/cascading/update_clusters_by_connected_components.def +0 -0
  132. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/cascading/update_clusters_by_connected_components.py +0 -0
  133. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/default/default_clusters_to_links.def +0 -0
  134. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/default/default_clusters_to_links.py +0 -0
  135. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/default/default_determining_exclusions.def +0 -0
  136. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/default/default_determining_exclusions.py +0 -0
  137. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/default/default_removing_records.def +0 -0
  138. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/default/default_removing_records.py +0 -0
  139. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/default/default_schema_alignment.def +0 -0
  140. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/default/default_schema_alignment.py +0 -0
  141. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/default/default_updating_clusters.def +0 -0
  142. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/default/default_updating_clusters.py +0 -0
  143. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/README.md +0 -0
  144. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/build-containers-local.sh +0 -0
  145. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/build-containers-remote.sh +0 -0
  146. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/input_data/create_input_files.ipynb +0 -0
  147. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/input_data/input_file_1.csv +0 -0
  148. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/input_data/input_file_1.parquet +0 -0
  149. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/input_data/input_file_2.csv +0 -0
  150. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/input_data/input_file_2.parquet +0 -0
  151. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/python_pandas/README.md +0 -0
  152. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/python_pandas/dummy_step.py +0 -0
  153. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/python_pandas/python_pandas.def +0 -0
  154. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/python_pyspark/README.md +0 -0
  155. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/python_pyspark/dummy_step.py +0 -0
  156. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/python_pyspark/python_pyspark.def +0 -0
  157. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/r/README.md +0 -0
  158. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/r/dummy_step.R +0 -0
  159. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/r/r-image.def +0 -0
  160. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/dev/test.py +0 -0
  161. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/example/middle_name_to_initial.def +0 -0
  162. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/example/middle_name_to_initial.py +0 -0
  163. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/fastLink/fastLink_evaluating_pairs.R +0 -0
  164. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/fastLink/fastLink_evaluating_pairs.def +0 -0
  165. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/fastLink/fastLink_links_to_clusters.R +0 -0
  166. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/fastLink/fastLink_links_to_clusters.def +0 -0
  167. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/output_dir/dummy_step_1_for_output_dir_example.def +0 -0
  168. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/output_dir/dummy_step_1_for_output_dir_example.py +0 -0
  169. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/output_dir/dummy_step_2_for_output_dir_example.def +0 -0
  170. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/output_dir/dummy_step_2_for_output_dir_example.py +0 -0
  171. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/rl-dummy/canonicalizing_and_downstream_analysis/dummy_canonicalizing_and_downstream_analysis.def +0 -0
  172. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/rl-dummy/canonicalizing_and_downstream_analysis/dummy_canonicalizing_and_downstream_analysis.py +0 -0
  173. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/rl-dummy/input_data/create_input_files.ipynb +0 -0
  174. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/rl-dummy/input_data/input_file_1.parquet +0 -0
  175. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/rl-dummy/input_data/input_file_2.parquet +0 -0
  176. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/rl-dummy/input_data/known_clusters.parquet +0 -0
  177. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/rl-dummy/pre-processing/dummy_pre-processing.def +0 -0
  178. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/rl-dummy/pre-processing/dummy_pre-processing.py +0 -0
  179. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/splink/splink_blocking_and_filtering.def +0 -0
  180. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/splink/splink_blocking_and_filtering.py +0 -0
  181. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/splink/splink_evaluating_pairs.def +0 -0
  182. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/splink/splink_evaluating_pairs.py +0 -0
  183. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/splink/splink_links_to_clusters.def +0 -0
  184. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/steps/splink/splink_links_to_clusters.py +0 -0
  185. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/utilities/__init__.py +0 -0
  186. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/utilities/aggregator_utils.py +0 -0
  187. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/utilities/data_utils.py +0 -0
  188. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/utilities/general_utils.py +0 -0
  189. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/utilities/paths.py +0 -0
  190. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/utilities/spark.smk +0 -0
  191. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/utilities/splitter_utils.py +0 -0
  192. {easylink-0.1.23 → easylink-0.1.24}/src/easylink/utilities/validation_utils.py +0 -0
  193. {easylink-0.1.23 → easylink-0.1.24}/src/easylink.egg-info/dependency_links.txt +0 -0
  194. {easylink-0.1.23 → easylink-0.1.24}/src/easylink.egg-info/entry_points.txt +0 -0
  195. {easylink-0.1.23 → easylink-0.1.24}/src/easylink.egg-info/not-zip-safe +0 -0
  196. {easylink-0.1.23 → easylink-0.1.24}/src/easylink.egg-info/requires.txt +0 -0
  197. {easylink-0.1.23 → easylink-0.1.24}/src/easylink.egg-info/top_level.txt +0 -0
  198. {easylink-0.1.23 → easylink-0.1.24}/tests/__init__.py +0 -0
  199. {easylink-0.1.23 → easylink-0.1.24}/tests/conftest.py +0 -0
  200. {easylink-0.1.23 → easylink-0.1.24}/tests/e2e/pipeline_improved_results.csv +0 -0
  201. {easylink-0.1.23 → easylink-0.1.24}/tests/e2e/pipeline_improved_results_2030.csv +0 -0
  202. {easylink-0.1.23 → easylink-0.1.24}/tests/e2e/pipeline_naive_results.csv +0 -0
  203. {easylink-0.1.23 → easylink-0.1.24}/tests/e2e/pipeline_splink_dummy_results.csv +0 -0
  204. {easylink-0.1.23 → easylink-0.1.24}/tests/e2e/test_pipelines_main_schema.py +0 -0
  205. {easylink-0.1.23 → easylink-0.1.24}/tests/e2e/test_step_types.py +0 -0
  206. {easylink-0.1.23 → easylink-0.1.24}/tests/integration/test_compositions.py +0 -0
  207. {easylink-0.1.23 → easylink-0.1.24}/tests/integration/test_data_utils.py +0 -0
  208. {easylink-0.1.23 → easylink-0.1.24}/tests/integration/test_snakemake.py +0 -0
  209. {easylink-0.1.23 → easylink-0.1.24}/tests/integration/test_snakemake_slurm.py +0 -0
  210. {easylink-0.1.23 → easylink-0.1.24}/tests/integration/test_snakemake_spark.py +0 -0
  211. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/common/environment_local.yaml +0 -0
  212. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/common/input_data.yaml +0 -0
  213. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/common/input_data_one_file.yaml +0 -0
  214. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/common/pipeline.yaml +0 -0
  215. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/e2e/environment_slurm.yaml +0 -0
  216. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/e2e/environment_slurm_4GB.yaml +0 -0
  217. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/e2e/input_data_dummy.yaml +0 -0
  218. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/e2e/pipeline.yaml +0 -0
  219. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/e2e/pipeline_expanded.yaml +0 -0
  220. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/integration/auto_parallel/pipeline_cloneable_step.yaml +0 -0
  221. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/integration/auto_parallel/pipeline_hierarchical_step.yaml +0 -0
  222. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/integration/auto_parallel/pipeline_loop_step.yaml +0 -0
  223. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/integration/environment_spark_slurm.yaml +0 -0
  224. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/integration/pipeline.yaml +0 -0
  225. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/integration/pipeline_output_dir.yaml +0 -0
  226. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/integration/pipeline_output_dir_default.yaml +0 -0
  227. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/integration/pipeline_spark.yaml +0 -0
  228. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/environment_minimum.yaml +0 -0
  229. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/environment_spark_slurm.yaml +0 -0
  230. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline.yaml +0 -0
  231. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_bad_combined_implementations.yaml +0 -0
  232. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_bad_implementation.yaml +0 -0
  233. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_bad_loop_formatting.yaml +0 -0
  234. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_bad_step.yaml +0 -0
  235. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_bad_type_key.yaml +0 -0
  236. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_combine_bad_implementation_names.yaml +0 -0
  237. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_combine_bad_topology.yaml +0 -0
  238. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_combine_two_steps.yaml +0 -0
  239. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_combine_with_extra_node.yaml +0 -0
  240. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_combine_with_iteration.yaml +0 -0
  241. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_combine_with_iteration_cycle.yaml +0 -0
  242. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_combine_with_missing_node.yaml +0 -0
  243. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_combine_with_parallel.yaml +0 -0
  244. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_default_implementations.yaml +0 -0
  245. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_missing_implementation_name.yaml +0 -0
  246. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_missing_implementations.yaml +0 -0
  247. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_missing_loop_nodes.yaml +0 -0
  248. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_missing_step.yaml +0 -0
  249. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_missing_substeps.yaml +0 -0
  250. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_missing_type_key.yaml +0 -0
  251. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_nested_templated_steps.yaml +0 -0
  252. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_out_of_order.yaml +0 -0
  253. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_spark.yaml +0 -0
  254. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_type_config_mismatch.yaml +0 -0
  255. {easylink-0.1.23 → easylink-0.1.24}/tests/specifications/unit/pipeline_wrong_clone_keys.yaml +0 -0
  256. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/__init__.py +0 -0
  257. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/conftest.py +0 -0
  258. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/recipe_strings/python_pandas.txt +0 -0
  259. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/rule_strings/aggregation_rule.txt +0 -0
  260. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/rule_strings/auto_parallel_rule.txt +0 -0
  261. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/rule_strings/checkpoint_rule.txt +0 -0
  262. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/rule_strings/implemented_rule_local.txt +0 -0
  263. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/rule_strings/implemented_rule_slurm.txt +0 -0
  264. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/rule_strings/pipeline_local.txt +0 -0
  265. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/rule_strings/pipeline_slurm.txt +0 -0
  266. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/rule_strings/target_rule.txt +0 -0
  267. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/rule_strings/validation_rule.txt +0 -0
  268. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/test_cli.py +0 -0
  269. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/test_config.py +0 -0
  270. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/test_data_utils.py +0 -0
  271. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/test_general_utils.py +0 -0
  272. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/test_graph_components.py +0 -0
  273. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/test_implementation.py +0 -0
  274. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/test_implementation_creator.py +0 -0
  275. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/test_pipeline.py +0 -0
  276. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/test_pipeline_graph.py +0 -0
  277. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/test_pipeline_schema.py +0 -0
  278. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/test_runner.py +0 -0
  279. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/test_step.py +0 -0
  280. {easylink-0.1.23 → easylink-0.1.24}/tests/unit/test_validations.py +0 -0
  281. {easylink-0.1.23 → easylink-0.1.24}/update_readme.py +0 -0
@@ -1,3 +1,7 @@
1
+ **0.1.24 - 6/26/25**
2
+
3
+ - Properly escape special characters in envars specified via pipeline configuration
4
+
1
5
  **0.1.23 - 6/25/25**
2
6
 
3
7
  - Remove "dummy" pipeline references from codebase
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easylink
3
- Version: 0.1.23
3
+ Version: 0.1.24
4
4
  Summary: Research repository for the EasyLink ER ecosystem project.
5
5
  Home-page: https://github.com/ihmeuw/easylink
6
6
  Author: The EasyLink developers
@@ -47,14 +47,14 @@ steps:
47
47
  implementation:
48
48
  name: splink_blocking_and_filtering
49
49
  configuration:
50
- BLOCKING_RULES: "'l.first_name == r.first_name,l.last_name == r.last_name'"
50
+ BLOCKING_RULES: "l.first_name == r.first_name,l.last_name == r.last_name"
51
51
  LINK_ONLY: true
52
52
  evaluating_pairs:
53
53
  implementation:
54
54
  name: splink_evaluating_pairs
55
55
  configuration:
56
- BLOCKING_RULES_FOR_TRAINING: "'l.first_name == r.first_name,l.last_name == r.last_name'"
57
- COMPARISONS: "'ssn:levenshtein,first_name:name,middle_initial:exact,last_name:name'"
56
+ BLOCKING_RULES_FOR_TRAINING: "l.first_name == r.first_name,l.last_name == r.last_name"
57
+ COMPARISONS: "ssn:levenshtein,first_name:name,middle_initial:exact,last_name:name"
58
58
  PROBABILITY_TWO_RANDOM_RECORDS_MATCH: 0.0001 # == 1 / len(w2)
59
59
  THRESHOLD_MATCH_PROBABILITY: 0
60
60
  LINK_ONLY: true
@@ -47,14 +47,14 @@ steps:
47
47
  implementation:
48
48
  name: splink_blocking_and_filtering
49
49
  configuration:
50
- BLOCKING_RULES: "'l.first_name == r.first_name,l.last_name == r.last_name'"
50
+ BLOCKING_RULES: "l.first_name == r.first_name,l.last_name == r.last_name"
51
51
  LINK_ONLY: true
52
52
  evaluating_pairs:
53
53
  implementation:
54
54
  name: splink_evaluating_pairs
55
55
  configuration:
56
- BLOCKING_RULES_FOR_TRAINING: "'l.first_name == r.first_name,l.last_name == r.last_name'"
57
- COMPARISONS: "'ssn:exact,first_name:exact,middle_initial:exact,last_name:exact'"
56
+ BLOCKING_RULES_FOR_TRAINING: "l.first_name == r.first_name,l.last_name == r.last_name"
57
+ COMPARISONS: "ssn:exact,first_name:exact,middle_initial:exact,last_name:exact"
58
58
  PROBABILITY_TWO_RANDOM_RECORDS_MATCH: 0.0001 # == 1 / len(w2)
59
59
  THRESHOLD_MATCH_PROBABILITY: 0
60
60
  LINK_ONLY: true
@@ -0,0 +1 @@
1
+ __version__ = "0.1.24"
@@ -243,6 +243,7 @@ def generate_dag(
243
243
  input_data=input_data,
244
244
  computing_environment=None,
245
245
  results_dir=results_dir,
246
+ images_dir=None,
246
247
  schema_name=schema,
247
248
  )
248
249
  logger.info("*** DAG saved to result directory ***")
@@ -14,6 +14,7 @@ dynamically as strings and appended to the Snakefile.
14
14
  """
15
15
 
16
16
  import os
17
+ import shlex
17
18
  from abc import ABC, abstractmethod
18
19
  from collections.abc import Callable
19
20
  from dataclasses import dataclass
@@ -204,7 +205,7 @@ rule:
204
205
  export SPARK_MASTER_URL"""
205
206
  for var_name, var_value in self.envvars.items():
206
207
  shell_cmd += f"""
207
- export {var_name}={var_value}"""
208
+ export {var_name}={shlex.quote(str(var_value))}"""
208
209
  # Log stdout/stderr to diagnostics directory
209
210
  shell_cmd += f"""
210
211
  {self.script_cmd} > {{log}} 2>&1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easylink
3
- Version: 0.1.23
3
+ Version: 0.1.24
4
4
  Summary: Research repository for the EasyLink ER ecosystem project.
5
5
  Home-page: https://github.com/ihmeuw/easylink
6
6
  Author: The EasyLink developers
@@ -194,7 +194,7 @@ tests/e2e/pipeline_improved_results.csv
194
194
  tests/e2e/pipeline_improved_results_2030.csv
195
195
  tests/e2e/pipeline_naive_results.csv
196
196
  tests/e2e/pipeline_splink_dummy_results.csv
197
- tests/e2e/test_easylink_run.py
197
+ tests/e2e/test_easylink_cli.py
198
198
  tests/e2e/test_pipelines_main_schema.py
199
199
  tests/e2e/test_step_types.py
200
200
  tests/integration/test_compositions.py
@@ -121,3 +121,26 @@ def test_easylink_run(
121
121
  "\n\n*** END OF TEST ***\n"
122
122
  f"[{pipeline_specification}, {input_data}, {computing_environment}]\n"
123
123
  )
124
+
125
+
126
+ @pytest.mark.skipif(
127
+ not is_on_slurm(),
128
+ reason="Must be on slurm to run this test.",
129
+ )
130
+ def test_easylink_generate_dag(test_specific_results_dir):
131
+ cmd = (
132
+ "easylink generate-dag "
133
+ f"-p {SPECIFICATIONS_DIR / 'e2e' / 'pipeline.yaml'} "
134
+ f"-i {SPECIFICATIONS_DIR / 'common' / 'input_data.yaml'} "
135
+ f"-o {str(test_specific_results_dir)} "
136
+ "--no-timestamp "
137
+ "--schema development "
138
+ )
139
+ subprocess.run(
140
+ cmd,
141
+ shell=True,
142
+ stdout=sys.stdout,
143
+ stderr=sys.stderr,
144
+ check=True,
145
+ )
146
+ assert (test_specific_results_dir / "DAG.svg").exists()
@@ -48,13 +48,13 @@ steps:
48
48
  implementation:
49
49
  name: splink_blocking_and_filtering
50
50
  configuration:
51
- BLOCKING_RULES: "'l.last_name == r.last_name'"
51
+ BLOCKING_RULES: "l.last_name == r.last_name"
52
52
  evaluating_pairs:
53
53
  implementation:
54
54
  name: splink_evaluating_pairs
55
55
  configuration:
56
- BLOCKING_RULES_FOR_TRAINING: "'l.first_name == r.first_name,l.last_name == r.last_name'"
57
- COMPARISONS: "'first_name:exact,last_name:exact'"
56
+ BLOCKING_RULES_FOR_TRAINING: "l.first_name == r.first_name,l.last_name == r.last_name"
57
+ COMPARISONS: "first_name:exact,last_name:exact"
58
58
  PROBABILITY_TWO_RANDOM_RECORDS_MATCH: 0.01
59
59
  THRESHOLD_MATCH_PROBABILITY: 0.1
60
60
  links_to_clusters:
@@ -112,13 +112,13 @@ steps:
112
112
  implementation:
113
113
  name: splink_blocking_and_filtering
114
114
  configuration:
115
- BLOCKING_RULES: "'l.first_name == r.first_name'"
115
+ BLOCKING_RULES: "l.first_name == r.first_name"
116
116
  evaluating_pairs:
117
117
  implementation:
118
118
  name: splink_evaluating_pairs
119
119
  configuration:
120
- BLOCKING_RULES_FOR_TRAINING: "'l.first_name == r.first_name,l.last_name == r.last_name'"
121
- COMPARISONS: "'first_name:exact,last_name:exact'"
120
+ BLOCKING_RULES_FOR_TRAINING: "l.first_name == r.first_name,l.last_name == r.last_name"
121
+ COMPARISONS: "first_name:exact,last_name:exact"
122
122
  PROBABILITY_TWO_RANDOM_RECORDS_MATCH: 0.01
123
123
  THRESHOLD_MATCH_PROBABILITY: 0.1
124
124
  links_to_clusters:
@@ -47,13 +47,13 @@ steps:
47
47
  implementation:
48
48
  name: splink_blocking_and_filtering
49
49
  configuration:
50
- BLOCKING_RULES: "'l.last_name == r.last_name'"
50
+ BLOCKING_RULES: "l.last_name == r.last_name"
51
51
  evaluating_pairs:
52
52
  implementation:
53
53
  name: splink_evaluating_pairs
54
54
  configuration:
55
- BLOCKING_RULES_FOR_TRAINING: "'l.first_name == r.first_name,l.last_name == r.last_name'"
56
- COMPARISONS: "'first_name:exact,last_name:exact'"
55
+ BLOCKING_RULES_FOR_TRAINING: "l.first_name == r.first_name,l.last_name == r.last_name"
56
+ COMPARISONS: "first_name:exact,last_name:exact"
57
57
  PROBABILITY_TWO_RANDOM_RECORDS_MATCH: 0.01
58
58
  THRESHOLD_MATCH_PROBABILITY: 0.1
59
59
  links_to_clusters:
@@ -47,12 +47,12 @@ steps:
47
47
  implementation:
48
48
  name: splink_blocking_and_filtering
49
49
  configuration:
50
- BLOCKING_RULES: "'l.last_name == r.last_name'"
50
+ BLOCKING_RULES: "l.last_name == r.last_name"
51
51
  evaluating_pairs:
52
52
  implementation:
53
53
  name: fastLink_evaluating_pairs
54
54
  configuration:
55
- COMPARISONS: "'first_name:exact,last_name:exact'"
55
+ COMPARISONS: "first_name:exact,last_name:exact"
56
56
  THRESHOLD_MATCH_PROBABILITY: 0.1
57
57
  links_to_clusters:
58
58
  implementation:
@@ -1,5 +1,6 @@
1
1
  # mypy: ignore-errors
2
2
  import os
3
+ import shlex
3
4
  from pathlib import Path
4
5
  from tempfile import TemporaryDirectory
5
6
 
@@ -246,6 +247,103 @@ def test_validation_rule_build_rule():
246
247
  _check_rule(rule, file_path)
247
248
 
248
249
 
250
+ @pytest.mark.parametrize(
251
+ "name, value, expected",
252
+ [
253
+ # Basic cases
254
+ ("SIMPLE_VAR", "simple_value", "export SIMPLE_VAR=simple_value"),
255
+ (
256
+ "VAR_WITH_SPACES",
257
+ "value with spaces",
258
+ "export VAR_WITH_SPACES='value with spaces'",
259
+ ),
260
+ # Quote cases
261
+ (
262
+ "VAR_WITH_SINGLE_QUOTES",
263
+ "value with 'single quotes'",
264
+ "export VAR_WITH_SINGLE_QUOTES='value with '\"'\"'single quotes'\"'\"''",
265
+ ),
266
+ (
267
+ "VAR_WITH_DOUBLE_QUOTES",
268
+ 'with "double" quotes',
269
+ "export VAR_WITH_DOUBLE_QUOTES='with \"double\" quotes'",
270
+ ),
271
+ (
272
+ "VAR_WITH_MIXED_QUOTES",
273
+ "with both \"double\" and 'single' quotes",
274
+ "export VAR_WITH_MIXED_QUOTES='with both \"double\" and '\"'\"'single'\"'\"' quotes'",
275
+ ),
276
+ # Complex structured data
277
+ (
278
+ "VAR_WITH_CHARS",
279
+ "col1='value1',col2='value2'",
280
+ "export VAR_WITH_CHARS='col1='\"'\"'value1'\"'\"',col2='\"'\"'value2'\"'\"''",
281
+ ),
282
+ (
283
+ "COMPLEX_VAR",
284
+ "l.first_name == r.first_name,l.last_name == r.last_name,col1='value1',col2='value2'",
285
+ "export COMPLEX_VAR='l.first_name == r.first_name,l.last_name == r.last_name,col1='\"'\"'value1'\"'\"',col2='\"'\"'value2'\"'\"''",
286
+ ),
287
+ # Edge cases that could cause shell injection or parsing issues
288
+ ("EMPTY_VAR", "", "export EMPTY_VAR=''"),
289
+ (
290
+ "SHELL_METACHAR_VAR",
291
+ "with$dollar`backtick",
292
+ "export SHELL_METACHAR_VAR='with$dollar`backtick'",
293
+ ),
294
+ (
295
+ "COMMAND_SEP_VAR",
296
+ "with;semicolon&ampersand",
297
+ "export COMMAND_SEP_VAR='with;semicolon&ampersand'",
298
+ ),
299
+ (
300
+ "PIPE_REDIRECT_VAR",
301
+ "with|pipe>redirect",
302
+ "export PIPE_REDIRECT_VAR='with|pipe>redirect'",
303
+ ),
304
+ (
305
+ "WHITESPACE_VAR",
306
+ "with\nnewline\ttab",
307
+ "export WHITESPACE_VAR='with\nnewline\ttab'",
308
+ ),
309
+ # Original bug cases
310
+ (
311
+ "BLOCKING_RULES",
312
+ "l.last_name == r.last_name",
313
+ "export BLOCKING_RULES='l.last_name == r.last_name'",
314
+ ),
315
+ (
316
+ "BLOCKING_RULES_FOR_TRAINING",
317
+ "l.first_name == r.first_name,l.last_name == r.last_name",
318
+ "export BLOCKING_RULES_FOR_TRAINING='l.first_name == r.first_name,l.last_name == r.last_name'",
319
+ ),
320
+ ],
321
+ )
322
+ def test_implemented_rule_envvar_quoting(name, value, expected):
323
+ """Test that environment variables with special characters are properly quoted in shell commands."""
324
+
325
+ rule = ImplementedRule(
326
+ name="test_rule",
327
+ step_name="test_step",
328
+ implementation_name="test_impl",
329
+ input_slots={},
330
+ validations=[],
331
+ output=["test_output.txt"],
332
+ resources=None,
333
+ envvars={name: value},
334
+ diagnostics_dir="test_diagnostics",
335
+ image_path="test.sif",
336
+ script_cmd="echo test",
337
+ requires_spark=False,
338
+ )
339
+
340
+ shell_cmd = rule._build_shell_cmd()
341
+
342
+ # Verify that each environment variable is properly quoted using shlex.quote
343
+ assert expected == f"export {name}={shlex.quote(str(value))}"
344
+ assert expected in shell_cmd
345
+
346
+
249
347
  ####################
250
348
  # Helper functions #
251
349
  ####################
@@ -1 +0,0 @@
1
- __version__ = "0.1.23"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes