opensafely 2.1.1__tar.gz → 2.1.2__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 (239) hide show
  1. {opensafely-2.1.1 → opensafely-2.1.2}/PKG-INFO +1 -1
  2. opensafely-2.1.2/opensafely/VERSION +1 -0
  3. opensafely-2.1.2/opensafely/_vendor/pipeline/constants.py +19 -0
  4. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/pipeline/features.py +2 -0
  5. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/pipeline/loading.py +1 -1
  6. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/pipeline/models.py +18 -84
  7. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/pipeline/validation.py +39 -38
  8. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely.egg-info/PKG-INFO +1 -1
  9. opensafely-2.1.1/opensafely/VERSION +0 -1
  10. opensafely-2.1.1/opensafely/_vendor/pipeline/constants.py +0 -21
  11. {opensafely-2.1.1 → opensafely-2.1.2}/LICENSE +0 -0
  12. {opensafely-2.1.1 → opensafely-2.1.2}/README.md +0 -0
  13. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/__init__.py +0 -0
  14. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/certifi/__init__.py +0 -0
  15. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/certifi/__main__.py +0 -0
  16. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/certifi/cacert.pem +0 -0
  17. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/certifi/core.py +0 -0
  18. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/certifi/py.typed +0 -0
  19. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/charset_normalizer/__init__.py +0 -0
  20. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/charset_normalizer/__main__.py +0 -0
  21. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/charset_normalizer/api.py +0 -0
  22. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/charset_normalizer/cd.py +0 -0
  23. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/charset_normalizer/cli/__init__.py +0 -0
  24. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/charset_normalizer/cli/__main__.py +0 -0
  25. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/charset_normalizer/constant.py +0 -0
  26. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/charset_normalizer/legacy.py +0 -0
  27. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/charset_normalizer/md.py +0 -0
  28. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/charset_normalizer/models.py +0 -0
  29. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/charset_normalizer/py.typed +0 -0
  30. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/charset_normalizer/utils.py +0 -0
  31. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/charset_normalizer/version.py +0 -0
  32. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/distro/__init__.py +0 -0
  33. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/distro/__main__.py +0 -0
  34. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/distro/distro.py +0 -0
  35. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/distro/py.typed +0 -0
  36. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/idna/__init__.py +0 -0
  37. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/idna/__main__.py +0 -0
  38. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/idna/cli.py +0 -0
  39. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/idna/codec.py +0 -0
  40. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/idna/compat.py +0 -0
  41. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/idna/core.py +0 -0
  42. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/idna/idnadata.py +0 -0
  43. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/idna/intranges.py +0 -0
  44. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/idna/package_data.py +0 -0
  45. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/idna/py.typed +0 -0
  46. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/idna/uts46data.py +0 -0
  47. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/_logs/__init__.py +0 -0
  48. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/_logs/_internal/__init__.py +0 -0
  49. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/_logs/py.typed +0 -0
  50. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/_logs/severity/__init__.py +0 -0
  51. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/attributes/__init__.py +0 -0
  52. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/attributes/py.typed +0 -0
  53. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/baggage/__init__.py +0 -0
  54. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/baggage/propagation/__init__.py +0 -0
  55. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/baggage/py.typed +0 -0
  56. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/context/__init__.py +0 -0
  57. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/context/context.py +0 -0
  58. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/context/contextvars_context.py +0 -0
  59. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/context/py.typed +0 -0
  60. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/environment_variables/__init__.py +0 -0
  61. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/environment_variables/py.typed +0 -0
  62. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/metrics/__init__.py +0 -0
  63. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/metrics/_internal/__init__.py +0 -0
  64. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/metrics/_internal/instrument.py +0 -0
  65. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/metrics/_internal/observation.py +0 -0
  66. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/metrics/py.typed +0 -0
  67. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/propagate/__init__.py +0 -0
  68. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/propagate/py.typed +0 -0
  69. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/propagators/_envcarrier.py +0 -0
  70. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/propagators/composite.py +0 -0
  71. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/propagators/py.typed +0 -0
  72. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/propagators/textmap.py +0 -0
  73. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/py.typed +0 -0
  74. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/trace/__init__.py +0 -0
  75. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/trace/propagation/__init__.py +0 -0
  76. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/trace/propagation/tracecontext.py +0 -0
  77. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/trace/py.typed +0 -0
  78. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/trace/span.py +0 -0
  79. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/trace/status.py +0 -0
  80. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/util/_decorator.py +0 -0
  81. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/util/_importlib_metadata.py +0 -0
  82. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/util/_once.py +0 -0
  83. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/util/_providers.py +0 -0
  84. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/util/py.typed +0 -0
  85. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/util/re.py +0 -0
  86. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/util/types.py +0 -0
  87. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/version/__init__.py +0 -0
  88. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/opentelemetry/version/py.typed +0 -0
  89. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/pipeline/__init__.py +1 -1
  90. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/pipeline/__main__.py +0 -0
  91. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/pipeline/exceptions.py +0 -0
  92. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/pipeline/legacy.py +0 -0
  93. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/pipeline/main.py +0 -0
  94. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/pipeline/outputs.py +0 -0
  95. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/pipeline.pyi +0 -0
  96. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/__init__.py +0 -0
  97. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/__version__.py +0 -0
  98. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/_internal_utils.py +0 -0
  99. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/_types.py +0 -0
  100. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/adapters.py +0 -0
  101. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/api.py +0 -0
  102. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/auth.py +0 -0
  103. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/certs.py +0 -0
  104. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/compat.py +0 -0
  105. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/cookies.py +0 -0
  106. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/exceptions.py +0 -0
  107. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/help.py +0 -0
  108. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/hooks.py +0 -0
  109. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/models.py +0 -0
  110. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/packages.py +0 -0
  111. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/py.typed +0 -0
  112. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/sessions.py +0 -0
  113. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/status_codes.py +0 -0
  114. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/structures.py +0 -0
  115. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/requests/utils.py +0 -0
  116. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/__init__.py +0 -0
  117. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/anchor.py +0 -0
  118. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/comments.py +0 -0
  119. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/compat.py +0 -0
  120. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/composer.py +0 -0
  121. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/configobjwalker.py +0 -0
  122. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/constructor.py +0 -0
  123. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/cyaml.py +0 -0
  124. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/dumper.py +0 -0
  125. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/emitter.py +0 -0
  126. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/error.py +0 -0
  127. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/events.py +0 -0
  128. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/loader.py +0 -0
  129. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/main.py +0 -0
  130. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/nodes.py +0 -0
  131. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/parser.py +0 -0
  132. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/py.typed +0 -0
  133. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/reader.py +0 -0
  134. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/representer.py +0 -0
  135. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/resolver.py +0 -0
  136. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/scalarbool.py +0 -0
  137. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/scalarfloat.py +0 -0
  138. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/scalarint.py +0 -0
  139. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/scalarstring.py +0 -0
  140. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/scanner.py +0 -0
  141. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/serializer.py +0 -0
  142. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/timestamp.py +0 -0
  143. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/tokens.py +0 -0
  144. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/ruyaml/util.py +0 -0
  145. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/typing_extensions.py +0 -0
  146. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/typing_extensions.pyi +0 -0
  147. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/__init__.py +0 -0
  148. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/_base_connection.py +0 -0
  149. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/_collections.py +0 -0
  150. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/_request_methods.py +0 -0
  151. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/_version.py +0 -0
  152. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/connection.py +0 -0
  153. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/connectionpool.py +0 -0
  154. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/contrib/__init__.py +0 -0
  155. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/contrib/emscripten/__init__.py +0 -0
  156. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/contrib/emscripten/connection.py +0 -0
  157. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/contrib/emscripten/fetch.py +0 -0
  158. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/contrib/emscripten/request.py +0 -0
  159. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/contrib/emscripten/response.py +0 -0
  160. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/contrib/pyopenssl.py +0 -0
  161. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/contrib/socks.py +0 -0
  162. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/exceptions.py +0 -0
  163. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/fields.py +0 -0
  164. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/filepost.py +0 -0
  165. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/http2/__init__.py +0 -0
  166. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/http2/connection.py +0 -0
  167. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/http2/probe.py +0 -0
  168. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/poolmanager.py +0 -0
  169. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/py.typed +0 -0
  170. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/response.py +0 -0
  171. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/util/__init__.py +0 -0
  172. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/util/connection.py +0 -0
  173. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/util/proxy.py +0 -0
  174. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/util/request.py +0 -0
  175. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/util/response.py +0 -0
  176. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/util/retry.py +0 -0
  177. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/util/ssl_.py +0 -0
  178. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/util/ssl_match_hostname.py +0 -0
  179. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/util/ssltransport.py +0 -0
  180. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/util/timeout.py +0 -0
  181. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/util/url.py +0 -0
  182. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/util/util.py +0 -0
  183. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/_vendor/urllib3/util/wait.py +0 -0
  184. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/clean.py +0 -0
  185. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/codelists.py +0 -0
  186. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/execute.py +0 -0
  187. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/info.py +0 -0
  188. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/__init__.py +0 -0
  189. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/actions.py +0 -0
  190. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/cli/__init__.py +0 -0
  191. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/cli/local_run.py +0 -0
  192. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/cli/migrate.py +0 -0
  193. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/config.py +0 -0
  194. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/create_or_update_jobs.py +0 -0
  195. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/executors/__init__.py +0 -0
  196. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/executors/local.py +0 -0
  197. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/executors/volumes.py +0 -0
  198. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/job_executor.py +0 -0
  199. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/lib/__init__.py +0 -0
  200. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/lib/database.py +0 -0
  201. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/lib/docker.py +0 -0
  202. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/lib/git.py +0 -0
  203. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/lib/github_validators.py +0 -0
  204. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/lib/log_utils.py +0 -0
  205. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/lib/lru_dict.py +0 -0
  206. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/lib/path_utils.py +0 -0
  207. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/lib/string_utils.py +0 -0
  208. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/lib/subprocess_utils.py +0 -0
  209. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/lib/yaml_utils.py +0 -0
  210. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/models.py +0 -0
  211. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/queries.py +0 -0
  212. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/reusable_actions.py +0 -0
  213. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/run.py +0 -0
  214. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jobrunner/tracing.py +0 -0
  215. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/jupyter.py +0 -0
  216. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/launch.py +0 -0
  217. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/pull.py +0 -0
  218. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/rstudio.py +0 -0
  219. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/unzip.py +0 -0
  220. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/upgrade.py +0 -0
  221. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely/utils.py +0 -0
  222. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely.egg-info/SOURCES.txt +0 -0
  223. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely.egg-info/dependency_links.txt +0 -0
  224. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely.egg-info/entry_points.txt +0 -0
  225. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely.egg-info/requires.txt +0 -0
  226. {opensafely-2.1.1 → opensafely-2.1.2}/opensafely.egg-info/top_level.txt +0 -0
  227. {opensafely-2.1.1 → opensafely-2.1.2}/pyproject.toml +0 -0
  228. {opensafely-2.1.1 → opensafely-2.1.2}/setup.cfg +0 -0
  229. {opensafely-2.1.1 → opensafely-2.1.2}/tests/test_clean.py +0 -0
  230. {opensafely-2.1.1 → opensafely-2.1.2}/tests/test_codelists.py +0 -0
  231. {opensafely-2.1.1 → opensafely-2.1.2}/tests/test_execute.py +0 -0
  232. {opensafely-2.1.1 → opensafely-2.1.2}/tests/test_info.py +0 -0
  233. {opensafely-2.1.1 → opensafely-2.1.2}/tests/test_init.py +0 -0
  234. {opensafely-2.1.1 → opensafely-2.1.2}/tests/test_launch.py +0 -0
  235. {opensafely-2.1.1 → opensafely-2.1.2}/tests/test_packaging.py +0 -0
  236. {opensafely-2.1.1 → opensafely-2.1.2}/tests/test_pull.py +0 -0
  237. {opensafely-2.1.1 → opensafely-2.1.2}/tests/test_unzip.py +0 -0
  238. {opensafely-2.1.1 → opensafely-2.1.2}/tests/test_upgrade.py +0 -0
  239. {opensafely-2.1.1 → opensafely-2.1.2}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opensafely
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: Command line tool for running OpenSAFELY studies locally.
5
5
  Author-email: OpenSAFELY <tech@opensafely.org>
6
6
  License: GPL-3.0
@@ -0,0 +1 @@
1
+ v2.1.2
@@ -0,0 +1,19 @@
1
+ # The magic action name which means "run every action"
2
+ RUN_ALL_COMMAND = "run_all"
3
+
4
+ LEVEL4_FILE_TYPES = {
5
+ # tables
6
+ ".csv",
7
+ # images
8
+ ".jpg",
9
+ ".jpeg",
10
+ ".png",
11
+ ".svg",
12
+ ".svgz",
13
+ # reports
14
+ ".html",
15
+ ".txt",
16
+ ".log",
17
+ ".json",
18
+ ".md",
19
+ }
@@ -15,6 +15,8 @@ FEATURE_FLAGS_BY_VERSION = {
15
15
 
16
16
  LATEST_VERSION = max([v[0] for v in FEATURE_FLAGS_BY_VERSION.values()])
17
17
 
18
+ MINIMUM_VERSION = 4
19
+
18
20
 
19
21
  def get_feature_flags_for_version(version: float) -> SimpleNamespace:
20
22
  if version > LATEST_VERSION:
@@ -80,7 +80,7 @@ def parse_yaml_file(data: str | Path, filename: str | None = None) -> dict[str,
80
80
  if FAST_PARSER is not None: # pragma: no cover
81
81
  try:
82
82
  return FAST_PARSER.load(data) # type: ignore[no-any-return]
83
- except Exception:
83
+ except Exception: # noqa: S110, BLE001
84
84
  pass
85
85
 
86
86
  try:
@@ -4,16 +4,15 @@ import pathlib
4
4
  import re
5
5
  import shlex
6
6
  import warnings
7
- from collections import defaultdict
8
7
  from dataclasses import dataclass
9
8
  from typing import Any
10
9
 
11
10
  from .constants import RUN_ALL_COMMAND
12
11
  from .exceptions import InvalidPatternError, ValidationError
13
- from .features import LATEST_VERSION, get_feature_flags_for_version
12
+ from .features import LATEST_VERSION, MINIMUM_VERSION, get_feature_flags_for_version
14
13
  from .validation import (
15
14
  validate_action_config,
16
- validate_cohortextractor_outputs,
15
+ validate_actions_config,
17
16
  validate_ehrql_outputs,
18
17
  validate_glob_pattern,
19
18
  validate_no_kwargs,
@@ -21,6 +20,7 @@ from .validation import (
21
20
  validate_not_latest_tag,
22
21
  validate_not_run_all_action,
23
22
  validate_type,
23
+ validate_unique_output_paths,
24
24
  )
25
25
 
26
26
 
@@ -28,7 +28,6 @@ from .validation import (
28
28
  DB_COMMANDS = {
29
29
  "ehrql": ("generate-dataset", "generate-measures"),
30
30
  "sqlrunner": "*", # all commands are valid
31
- "cohortextractor": ("generate_cohort", "generate_codelist_report"),
32
31
  "databuilder": ("generate-dataset",),
33
32
  }
34
33
 
@@ -54,26 +53,6 @@ def is_database_action(args: list[str]) -> bool:
54
53
  return args[1] in db_commands
55
54
 
56
55
 
57
- @dataclass(frozen=True)
58
- class Expectations:
59
- population_size: int
60
-
61
- @classmethod
62
- def build(
63
- cls,
64
- population_size: Any = None,
65
- **kwargs: Any,
66
- ) -> Expectations:
67
- validate_no_kwargs(kwargs, "project `expectations` section")
68
- try:
69
- population_size = int(population_size)
70
- except (TypeError, ValueError):
71
- raise ValidationError(
72
- "Project expectations population size must be a number",
73
- )
74
- return cls(population_size)
75
-
76
-
77
56
  @dataclass(frozen=True)
78
57
  class Outputs:
79
58
  highly_sensitive: dict[str, str] | None
@@ -95,7 +74,7 @@ class Outputs:
95
74
  and minimally_sensitive is None
96
75
  ):
97
76
  raise ValidationError(
98
- f"must specify at least one output of: {', '.join(['highly_sensitive', 'moderately_sensitive', 'minimally_sensitive'])}"
77
+ "must specify at least one output of: highly_sensitive, moderately_sensitive, minimally_sensitive"
99
78
  )
100
79
 
101
80
  validate_no_kwargs(kwargs, f"`outputs` section for action {action_id}")
@@ -210,8 +189,6 @@ class Action:
210
189
  )
211
190
  action = cls(action_id, outputs, run, needs, config, dummy_data_file)
212
191
 
213
- if re.match(r"cohortextractor:\S+ generate_cohort", run.raw):
214
- validate_cohortextractor_outputs(action_id, action)
215
192
  if match := re.match(
216
193
  r"(ehrql|databuilder):\S+ generate[-_](?P<ehrql_type>dataset|measures)",
217
194
  run.raw,
@@ -252,17 +229,14 @@ class Action:
252
229
  class Pipeline:
253
230
  version: float
254
231
  actions: dict[str, Action]
255
- expectations: Expectations | None
256
232
 
257
233
  @classmethod
258
234
  def build(
259
235
  cls,
260
236
  version: Any = None,
261
237
  actions: Any = None,
262
- expectations: Any = None,
263
238
  **kwargs: Any,
264
239
  ) -> Pipeline:
265
- validate_no_kwargs(kwargs, "project")
266
240
  if version is None:
267
241
  raise ValidationError(
268
242
  f"Project file must have a `version` attribute specifying which "
@@ -274,84 +248,44 @@ class Pipeline:
274
248
  version = float(version)
275
249
  except (TypeError, ValueError):
276
250
  raise ValidationError(
277
- f"`version` must be a number between 1 and {LATEST_VERSION}"
251
+ f"`version` must be a number between {MINIMUM_VERSION} and {LATEST_VERSION}"
278
252
  )
279
253
  else:
254
+ if version < MINIMUM_VERSION:
255
+ raise ValidationError(
256
+ f"Your project file is using a deprecated version ({version}); update to at least version {MINIMUM_VERSION}"
257
+ )
280
258
  if version != LATEST_VERSION:
281
259
  warnings.warn(
282
260
  f"ProjectWarning: Your project file is using an old version ({version}); consider updating to version {LATEST_VERSION}",
283
261
  stacklevel=2,
284
262
  )
285
263
 
264
+ validate_no_kwargs(kwargs, "project")
265
+
286
266
  feat = get_feature_flags_for_version(version)
287
267
 
288
268
  validate_type(actions, dict, "Project `actions` section")
289
269
 
290
- _actions = dict()
270
+ _actions = {}
291
271
  for action_id, action_config in actions.items():
292
272
  validate_not_run_all_action(action_id, feat)
293
273
  validate_action_config(action_id, action_config)
294
274
  _actions[action_id] = Action.build(action_id, **action_config)
295
275
  actions = _actions
296
276
 
297
- if feat.REMOVE_SUPPORT_FOR_COHORT_EXTRACTOR:
298
- for config in actions.values():
299
- validate_not_cohort_extractor_action(config)
277
+ for config in actions.values():
278
+ validate_not_cohort_extractor_action(config)
300
279
 
301
280
  if feat.REMOVE_SUPPORT_FOR_LATEST_TAG:
302
281
  for config in actions.values():
303
282
  validate_not_latest_tag(config)
304
283
 
305
- seen: dict[Command, list[str]] = defaultdict(list)
306
- for name, config in actions.items():
307
- run = config.run
308
- if run in seen:
309
- raise ValidationError(
310
- f"Action {name} has the same 'run' command as other actions: {' ,'.join(seen[run])}"
311
- )
312
- seen[run].append(name)
284
+ validate_actions_config(actions)
313
285
 
314
- if feat.UNIQUE_OUTPUT_PATH:
315
- # find duplicate paths defined in the outputs section
316
- seen_files = []
317
- for config in actions.values():
318
- for output in config.outputs.dict().values():
319
- for filename in output.values():
320
- if filename in seen_files:
321
- raise ValidationError(
322
- f"Output path {filename} is not unique"
323
- )
324
-
325
- seen_files.append(filename)
326
-
327
- for a in actions.values():
328
- for n in a.needs:
329
- if n not in actions:
330
- raise ValidationError(
331
- f"Action `{a.action_id}` references an unknown action in its `needs` list: {n}"
332
- )
333
-
334
- if feat.REMOVE_SUPPORT_FOR_COHORT_EXTRACTOR:
335
- if expectations is not None:
336
- raise ValidationError(
337
- "Project includes `expectations` section, which is not supported in this version. "
338
- "This section is only applicable to deprecated cohortextractor actions; you can safely remove it."
339
- )
340
- elif feat.EXPECTATIONS_POPULATION:
341
- if expectations is None:
342
- raise ValidationError("Project must include `expectations` section")
343
- else:
344
- expectations = {"population_size": 1000}
345
-
346
- if expectations is not None:
347
- validate_type(expectations, dict, "Project `expectations` section")
348
- if "population_size" not in expectations:
349
- raise ValidationError(
350
- "Project `expectations` section must include `population_size` section",
351
- )
352
- expectations = Expectations.build(**expectations)
286
+ validate_unique_output_paths(actions)
353
287
 
354
- return cls(version, actions, expectations)
288
+ return cls(version, actions)
355
289
 
356
290
  @property
357
291
  def all_actions(self) -> list[str]:
@@ -363,7 +297,7 @@ class Pipeline:
363
297
  We use a list comprehension rather than set operators as previously so we preserve
364
298
  the original order.
365
299
  """
366
- return [action for action in self.actions.keys() if action != RUN_ALL_COMMAND]
300
+ return [action for action in self.actions if action != RUN_ALL_COMMAND]
367
301
 
368
302
  @property
369
303
  def action_images(self) -> set[str]:
@@ -3,17 +3,17 @@ from __future__ import annotations
3
3
  import fnmatch
4
4
  import posixpath
5
5
  import warnings
6
+ from collections import defaultdict
6
7
  from pathlib import Path, PurePath, PurePosixPath, PureWindowsPath
7
8
  from types import SimpleNamespace
8
9
  from typing import TYPE_CHECKING, Any
9
10
 
10
11
  from .constants import LEVEL4_FILE_TYPES
11
12
  from .exceptions import InvalidPatternError, ValidationError
12
- from .outputs import get_output_dirs
13
13
 
14
14
 
15
15
  if TYPE_CHECKING: # pragma: no cover
16
- from .models import Action
16
+ from .models import Action, Command
17
17
 
18
18
 
19
19
  def validate_type(val: Any, exp_type: type, loc: str, optional: bool = False) -> None:
@@ -59,11 +59,10 @@ def validate_glob_pattern(pattern: str, privacy_level: str) -> None:
59
59
  "output paths must have a file type extension at the end"
60
60
  )
61
61
 
62
- if privacy_level == "moderately_sensitive":
63
- if path.suffix not in LEVEL4_FILE_TYPES:
64
- raise InvalidPatternError(
65
- f"{path} is not an allowed file type for moderately_sensitive outputs"
66
- )
62
+ if privacy_level == "moderately_sensitive" and path.suffix not in LEVEL4_FILE_TYPES:
63
+ raise InvalidPatternError(
64
+ f"{path} is not an allowed file type for moderately_sensitive outputs"
65
+ )
67
66
 
68
67
  # Check that the path is in normal form
69
68
  if posixpath.normpath(pattern) != pattern:
@@ -92,6 +91,29 @@ def validate_action_config(action_id: str, action_config: Any) -> None:
92
91
  )
93
92
 
94
93
 
94
+ def validate_actions_config(actions: dict[str, Action]) -> None:
95
+ """
96
+ Validates that actions are consistent and valid with respect to other actions
97
+ - run commands are not duplicated
98
+ - Specified `needs` are valid actions
99
+ """
100
+ seen: dict[Command, list[str]] = defaultdict(list)
101
+ for name, config in actions.items():
102
+ run = config.run
103
+ if run in seen:
104
+ raise ValidationError(
105
+ f"Action {name} has the same 'run' command as other actions: {' ,'.join(seen[run])}"
106
+ )
107
+ seen[run].append(name)
108
+
109
+ for a in actions.values():
110
+ for n in a.needs:
111
+ if n not in actions:
112
+ raise ValidationError(
113
+ f"Action `{a.action_id}` references an unknown action in its `needs` list: {n}"
114
+ )
115
+
116
+
95
117
  def validate_not_cohort_extractor_action(action: Action) -> None:
96
118
  if action.run.parts[0].startswith("cohortextractor"):
97
119
  raise ValidationError(
@@ -121,39 +143,18 @@ def validate_not_latest_tag(action: Action) -> None:
121
143
  )
122
144
 
123
145
 
124
- def validate_cohortextractor_outputs(action_id: str, action: Action) -> None:
146
+ def validate_unique_output_paths(actions: dict[str, Action]) -> None:
125
147
  """
126
- Check cohortextractor's output config is valid for this command
127
-
128
- We can't validate outputs in the Action or Outputs models because we need
129
- to look up other fields (eg run).
148
+ Validates that paths defined in the outputs section are unique
130
149
  """
131
- # ensure we only have output level defined.
132
- num_output_levels = len(action.outputs)
133
- if num_output_levels != 1:
134
- raise ValidationError(
135
- "A `generate_cohort` action must have exactly one output; "
136
- f"{action_id} had {num_output_levels}"
137
- )
138
-
139
- output_dirs = get_output_dirs(action.outputs)
140
- if len(output_dirs) == 1:
141
- return
142
-
143
- # If we detect multiple output directories but the command explicitly
144
- # specifies an output directory then we assume the user knows what
145
- # they're doing and don't attempt to modify the output directory or
146
- # throw an error
147
- flag = "--output-dir"
148
- has_output_dir = any(
149
- arg == flag or arg.startswith(f"{flag}=") for arg in action.run.parts
150
- )
151
- if not has_output_dir:
152
- raise ValidationError(
153
- f"generate_cohort command should produce output in only one "
154
- f"directory, found {len(output_dirs)}:\n"
155
- + "\n".join([f" - {d}/" for d in output_dirs])
156
- )
150
+ seen_files = []
151
+ for config in actions.values():
152
+ for output in config.outputs.dict().values():
153
+ for filename in output.values():
154
+ if filename in seen_files:
155
+ raise ValidationError(f"Output path {filename} is not unique")
156
+
157
+ seen_files.append(filename)
157
158
 
158
159
 
159
160
  def validate_ehrql_outputs(action_id: str, action: Action, ehrql_type: str) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opensafely
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: Command line tool for running OpenSAFELY studies locally.
5
5
  Author-email: OpenSAFELY <tech@opensafely.org>
6
6
  License: GPL-3.0
@@ -1 +0,0 @@
1
- v2.1.1
@@ -1,21 +0,0 @@
1
- # The magic action name which means "run every action"
2
- RUN_ALL_COMMAND = "run_all"
3
-
4
- LEVEL4_FILE_TYPES = set(
5
- [
6
- # tables
7
- ".csv",
8
- # images
9
- ".jpg",
10
- ".jpeg",
11
- ".png",
12
- ".svg",
13
- ".svgz",
14
- # reports
15
- ".html",
16
- ".txt",
17
- ".log",
18
- ".json",
19
- ".md",
20
- ]
21
- )
File without changes
File without changes
@@ -6,8 +6,8 @@ from .main import load_pipeline
6
6
 
7
7
 
8
8
  __all__ = [
9
- "ProjectValidationError",
10
9
  "RUN_ALL_COMMAND",
10
+ "ProjectValidationError",
11
11
  "YAMLError",
12
12
  "load_pipeline",
13
13
  ]