ob-metaflow 2.15.13.1__py2.py3-none-any.whl → 2.19.7.1rc0__py2.py3-none-any.whl

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 (169) hide show
  1. metaflow/__init__.py +10 -3
  2. metaflow/_vendor/imghdr/__init__.py +186 -0
  3. metaflow/_vendor/yaml/__init__.py +427 -0
  4. metaflow/_vendor/yaml/composer.py +139 -0
  5. metaflow/_vendor/yaml/constructor.py +748 -0
  6. metaflow/_vendor/yaml/cyaml.py +101 -0
  7. metaflow/_vendor/yaml/dumper.py +62 -0
  8. metaflow/_vendor/yaml/emitter.py +1137 -0
  9. metaflow/_vendor/yaml/error.py +75 -0
  10. metaflow/_vendor/yaml/events.py +86 -0
  11. metaflow/_vendor/yaml/loader.py +63 -0
  12. metaflow/_vendor/yaml/nodes.py +49 -0
  13. metaflow/_vendor/yaml/parser.py +589 -0
  14. metaflow/_vendor/yaml/reader.py +185 -0
  15. metaflow/_vendor/yaml/representer.py +389 -0
  16. metaflow/_vendor/yaml/resolver.py +227 -0
  17. metaflow/_vendor/yaml/scanner.py +1435 -0
  18. metaflow/_vendor/yaml/serializer.py +111 -0
  19. metaflow/_vendor/yaml/tokens.py +104 -0
  20. metaflow/cards.py +4 -0
  21. metaflow/cli.py +125 -21
  22. metaflow/cli_components/init_cmd.py +1 -0
  23. metaflow/cli_components/run_cmds.py +204 -40
  24. metaflow/cli_components/step_cmd.py +160 -4
  25. metaflow/client/__init__.py +1 -0
  26. metaflow/client/core.py +198 -130
  27. metaflow/client/filecache.py +59 -32
  28. metaflow/cmd/code/__init__.py +2 -1
  29. metaflow/cmd/develop/stub_generator.py +49 -18
  30. metaflow/cmd/develop/stubs.py +9 -27
  31. metaflow/cmd/make_wrapper.py +30 -0
  32. metaflow/datastore/__init__.py +1 -0
  33. metaflow/datastore/content_addressed_store.py +40 -9
  34. metaflow/datastore/datastore_set.py +10 -1
  35. metaflow/datastore/flow_datastore.py +124 -4
  36. metaflow/datastore/spin_datastore.py +91 -0
  37. metaflow/datastore/task_datastore.py +92 -6
  38. metaflow/debug.py +5 -0
  39. metaflow/decorators.py +331 -82
  40. metaflow/extension_support/__init__.py +414 -356
  41. metaflow/extension_support/_empty_file.py +2 -2
  42. metaflow/flowspec.py +322 -82
  43. metaflow/graph.py +178 -15
  44. metaflow/includefile.py +25 -3
  45. metaflow/lint.py +94 -3
  46. metaflow/meta_files.py +13 -0
  47. metaflow/metadata_provider/metadata.py +13 -2
  48. metaflow/metaflow_config.py +66 -4
  49. metaflow/metaflow_environment.py +91 -25
  50. metaflow/metaflow_profile.py +18 -0
  51. metaflow/metaflow_version.py +16 -1
  52. metaflow/package/__init__.py +673 -0
  53. metaflow/packaging_sys/__init__.py +880 -0
  54. metaflow/packaging_sys/backend.py +128 -0
  55. metaflow/packaging_sys/distribution_support.py +153 -0
  56. metaflow/packaging_sys/tar_backend.py +99 -0
  57. metaflow/packaging_sys/utils.py +54 -0
  58. metaflow/packaging_sys/v1.py +527 -0
  59. metaflow/parameters.py +6 -2
  60. metaflow/plugins/__init__.py +6 -0
  61. metaflow/plugins/airflow/airflow.py +11 -1
  62. metaflow/plugins/airflow/airflow_cli.py +16 -5
  63. metaflow/plugins/argo/argo_client.py +42 -20
  64. metaflow/plugins/argo/argo_events.py +6 -6
  65. metaflow/plugins/argo/argo_workflows.py +1023 -344
  66. metaflow/plugins/argo/argo_workflows_cli.py +396 -94
  67. metaflow/plugins/argo/argo_workflows_decorator.py +9 -0
  68. metaflow/plugins/argo/argo_workflows_deployer_objects.py +75 -49
  69. metaflow/plugins/argo/capture_error.py +5 -2
  70. metaflow/plugins/argo/conditional_input_paths.py +35 -0
  71. metaflow/plugins/argo/exit_hooks.py +209 -0
  72. metaflow/plugins/argo/param_val.py +19 -0
  73. metaflow/plugins/aws/aws_client.py +6 -0
  74. metaflow/plugins/aws/aws_utils.py +33 -1
  75. metaflow/plugins/aws/batch/batch.py +72 -5
  76. metaflow/plugins/aws/batch/batch_cli.py +24 -3
  77. metaflow/plugins/aws/batch/batch_decorator.py +57 -6
  78. metaflow/plugins/aws/step_functions/step_functions.py +28 -3
  79. metaflow/plugins/aws/step_functions/step_functions_cli.py +49 -4
  80. metaflow/plugins/aws/step_functions/step_functions_deployer.py +3 -0
  81. metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +30 -0
  82. metaflow/plugins/cards/card_cli.py +20 -1
  83. metaflow/plugins/cards/card_creator.py +24 -1
  84. metaflow/plugins/cards/card_datastore.py +21 -49
  85. metaflow/plugins/cards/card_decorator.py +58 -6
  86. metaflow/plugins/cards/card_modules/basic.py +38 -9
  87. metaflow/plugins/cards/card_modules/bundle.css +1 -1
  88. metaflow/plugins/cards/card_modules/chevron/renderer.py +1 -1
  89. metaflow/plugins/cards/card_modules/components.py +592 -3
  90. metaflow/plugins/cards/card_modules/convert_to_native_type.py +34 -5
  91. metaflow/plugins/cards/card_modules/json_viewer.py +232 -0
  92. metaflow/plugins/cards/card_modules/main.css +1 -0
  93. metaflow/plugins/cards/card_modules/main.js +56 -41
  94. metaflow/plugins/cards/card_modules/test_cards.py +22 -6
  95. metaflow/plugins/cards/component_serializer.py +1 -8
  96. metaflow/plugins/cards/metadata.py +22 -0
  97. metaflow/plugins/catch_decorator.py +9 -0
  98. metaflow/plugins/datastores/local_storage.py +12 -6
  99. metaflow/plugins/datastores/spin_storage.py +12 -0
  100. metaflow/plugins/datatools/s3/s3.py +49 -17
  101. metaflow/plugins/datatools/s3/s3op.py +113 -66
  102. metaflow/plugins/env_escape/client_modules.py +102 -72
  103. metaflow/plugins/events_decorator.py +127 -121
  104. metaflow/plugins/exit_hook/__init__.py +0 -0
  105. metaflow/plugins/exit_hook/exit_hook_decorator.py +46 -0
  106. metaflow/plugins/exit_hook/exit_hook_script.py +52 -0
  107. metaflow/plugins/kubernetes/kubernetes.py +12 -1
  108. metaflow/plugins/kubernetes/kubernetes_cli.py +11 -0
  109. metaflow/plugins/kubernetes/kubernetes_decorator.py +25 -6
  110. metaflow/plugins/kubernetes/kubernetes_job.py +12 -4
  111. metaflow/plugins/kubernetes/kubernetes_jobsets.py +31 -30
  112. metaflow/plugins/metadata_providers/local.py +76 -82
  113. metaflow/plugins/metadata_providers/service.py +13 -9
  114. metaflow/plugins/metadata_providers/spin.py +16 -0
  115. metaflow/plugins/package_cli.py +36 -24
  116. metaflow/plugins/parallel_decorator.py +11 -2
  117. metaflow/plugins/parsers.py +16 -0
  118. metaflow/plugins/pypi/bootstrap.py +7 -1
  119. metaflow/plugins/pypi/conda_decorator.py +41 -82
  120. metaflow/plugins/pypi/conda_environment.py +14 -6
  121. metaflow/plugins/pypi/micromamba.py +9 -1
  122. metaflow/plugins/pypi/pip.py +41 -5
  123. metaflow/plugins/pypi/pypi_decorator.py +4 -4
  124. metaflow/plugins/pypi/utils.py +22 -0
  125. metaflow/plugins/secrets/__init__.py +3 -0
  126. metaflow/plugins/secrets/secrets_decorator.py +14 -178
  127. metaflow/plugins/secrets/secrets_func.py +49 -0
  128. metaflow/plugins/secrets/secrets_spec.py +101 -0
  129. metaflow/plugins/secrets/utils.py +74 -0
  130. metaflow/plugins/test_unbounded_foreach_decorator.py +2 -2
  131. metaflow/plugins/timeout_decorator.py +0 -1
  132. metaflow/plugins/uv/bootstrap.py +29 -1
  133. metaflow/plugins/uv/uv_environment.py +5 -3
  134. metaflow/pylint_wrapper.py +5 -1
  135. metaflow/runner/click_api.py +79 -26
  136. metaflow/runner/deployer.py +208 -6
  137. metaflow/runner/deployer_impl.py +32 -12
  138. metaflow/runner/metaflow_runner.py +266 -33
  139. metaflow/runner/subprocess_manager.py +21 -1
  140. metaflow/runner/utils.py +27 -16
  141. metaflow/runtime.py +660 -66
  142. metaflow/task.py +255 -26
  143. metaflow/user_configs/config_options.py +33 -21
  144. metaflow/user_configs/config_parameters.py +220 -58
  145. metaflow/user_decorators/__init__.py +0 -0
  146. metaflow/user_decorators/common.py +144 -0
  147. metaflow/user_decorators/mutable_flow.py +512 -0
  148. metaflow/user_decorators/mutable_step.py +424 -0
  149. metaflow/user_decorators/user_flow_decorator.py +264 -0
  150. metaflow/user_decorators/user_step_decorator.py +749 -0
  151. metaflow/util.py +197 -7
  152. metaflow/vendor.py +23 -7
  153. metaflow/version.py +1 -1
  154. {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/Makefile +13 -2
  155. {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/Tiltfile +107 -7
  156. {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/pick_services.sh +1 -0
  157. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/METADATA +2 -3
  158. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/RECORD +162 -121
  159. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/WHEEL +1 -1
  160. metaflow/_vendor/v3_5/__init__.py +0 -1
  161. metaflow/_vendor/v3_5/importlib_metadata/__init__.py +0 -644
  162. metaflow/_vendor/v3_5/importlib_metadata/_compat.py +0 -152
  163. metaflow/_vendor/v3_5/zipp.py +0 -329
  164. metaflow/info_file.py +0 -25
  165. metaflow/package.py +0 -203
  166. metaflow/user_configs/config_decorators.py +0 -568
  167. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/entry_points.txt +0 -0
  168. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/licenses/LICENSE +0 -0
  169. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/top_level.txt +0 -0
@@ -11,8 +11,10 @@ from collections import defaultdict, namedtuple
11
11
  from importlib.abc import MetaPathFinder, Loader
12
12
  from itertools import chain
13
13
  from pathlib import Path
14
+ from typing import Any, Dict
14
15
 
15
- from metaflow.info_file import read_info_file
16
+ from metaflow.meta_files import read_info_file
17
+ from metaflow.util import walk_without_cycles
16
18
 
17
19
 
18
20
  #
@@ -82,6 +84,7 @@ EXT_CONFIG_REGEXP = re.compile(r"^mfextinit_[a-zA-Z0-9_-]+\.py$")
82
84
  EXT_META_REGEXP = re.compile(r"^mfextmeta_[a-zA-Z0-9_-]+\.py$")
83
85
  REQ_NAME = re.compile(r"^(([a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])|[a-zA-Z0-9]).*$")
84
86
  EXT_EXCLUDE_SUFFIXES = [".pyc"]
87
+ FINDER_TRANS = str.maketrans(".-", "__")
85
88
 
86
89
  # To get verbose messages, set METAFLOW_DEBUG_EXT to 1
87
90
  DEBUG_EXT = os.environ.get("METAFLOW_DEBUG_EXT", False)
@@ -103,9 +106,6 @@ def load_module(module_name):
103
106
 
104
107
  def get_modules(extension_point):
105
108
  modules_to_load = []
106
- if not _mfext_supported:
107
- _ext_debug("Not supported for your Python version -- 3.4+ is needed")
108
- return []
109
109
  if extension_point not in _extension_points:
110
110
  raise RuntimeError(
111
111
  "Metaflow extension point '%s' not supported" % extension_point
@@ -130,7 +130,6 @@ def dump_module_info(all_packages=None, pkgs_per_extension_point=None):
130
130
  if pkgs_per_extension_point is None:
131
131
  pkgs_per_extension_point = _pkgs_per_extension_point
132
132
 
133
- _filter_files_all(all_packages)
134
133
  sanitized_all_packages = dict()
135
134
  # Strip out root_paths (we don't need it and no need to expose user's dir structure)
136
135
  for k, v in all_packages.items():
@@ -138,6 +137,7 @@ def dump_module_info(all_packages=None, pkgs_per_extension_point=None):
138
137
  "root_paths": None,
139
138
  "meta_module": v["meta_module"],
140
139
  "files": v["files"],
140
+ "full_path_files": None,
141
141
  "version": v["version"],
142
142
  "package_version": v.get("package_version", "<unk>"),
143
143
  "extension_name": v.get("extension_name", "<unk>"),
@@ -146,9 +146,6 @@ def dump_module_info(all_packages=None, pkgs_per_extension_point=None):
146
146
 
147
147
 
148
148
  def get_extensions_in_dir(d):
149
- if not _mfext_supported:
150
- _ext_debug("Not supported for your Python version -- 3.4+ is needed")
151
- return None, None
152
149
  return _get_extension_packages(ignore_info_file=True, restrict_to_directories=[d])
153
150
 
154
151
 
@@ -193,17 +190,25 @@ def package_mfext_package(package_name):
193
190
 
194
191
  _ext_debug("Packaging '%s'" % package_name)
195
192
  pkg_info = _all_packages.get(package_name, None)
196
- _filter_files_package(pkg_info)
193
+
197
194
  if pkg_info and pkg_info.get("root_paths", None):
198
- single_path = len(pkg_info["root_paths"]) == 1
199
- for p in pkg_info["root_paths"]:
200
- root_path = to_unicode(p)
201
- for f in pkg_info["files"]:
202
- f_unicode = to_unicode(f)
203
- fp = os.path.join(root_path, f_unicode)
204
- if single_path or os.path.isfile(fp):
205
- _ext_debug(" Adding '%s'" % fp)
206
- yield fp, os.path.join(EXT_PKG, f_unicode)
195
+ if pkg_info["full_path_files"]:
196
+ # Case for initial packaging
197
+ for f, short_name in zip(pkg_info["full_path_files"], pkg_info["files"]):
198
+ f_unicode = os.path.join(EXT_PKG, to_unicode(short_name))
199
+ _ext_debug(" Adding '%s' as '%s'" % (f, f_unicode))
200
+ yield f, f_unicode
201
+ else:
202
+ # When re-packaging (ie: packaging Metaflow from a Metaflow run):
203
+ single_path = len(pkg_info["root_paths"]) == 1
204
+ for p in pkg_info["root_paths"]:
205
+ root_path = to_unicode(p)
206
+ for f in pkg_info["files"]:
207
+ f_unicode = to_unicode(f)
208
+ fp = os.path.join(root_path, f_unicode)
209
+ if single_path or os.path.isfile(fp):
210
+ _ext_debug(" Adding '%s'" % fp)
211
+ yield fp, os.path.join(EXT_PKG, f_unicode)
207
212
 
208
213
 
209
214
  def package_mfext_all():
@@ -211,13 +216,17 @@ def package_mfext_all():
211
216
  # the packaged metaflow_extensions directory "self-contained" so that
212
217
  # python doesn't go and search other parts of the system for more
213
218
  # metaflow_extensions.
214
- yield os.path.join(
215
- os.path.dirname(os.path.abspath(__file__)), "_empty_file.py"
216
- ), os.path.join(EXT_PKG, "__init__.py")
219
+ if _all_packages:
220
+ yield os.path.join(
221
+ os.path.dirname(os.path.abspath(__file__)), "_empty_file.py"
222
+ ), os.path.join(EXT_PKG, "__init__.py")
217
223
 
218
224
  for p in _all_packages:
219
- for path_tuple in package_mfext_package(p):
220
- yield path_tuple
225
+ yield from package_mfext_package(p)
226
+
227
+
228
+ def package_mfext_all_descriptions():
229
+ return _all_packages
221
230
 
222
231
 
223
232
  def load_globals(module, dst_globals, extra_indent=False):
@@ -312,21 +321,16 @@ def multiload_all(modules, extension_point, dst_globals):
312
321
 
313
322
 
314
323
  _py_ver = sys.version_info[:2]
315
- _mfext_supported = False
316
324
  _aliased_modules = []
317
325
 
318
- if _py_ver >= (3, 4):
319
- import importlib.util
326
+ import importlib.util
320
327
 
321
- if _py_ver >= (3, 8):
322
- from importlib import metadata
323
- elif _py_ver >= (3, 7):
324
- from metaflow._vendor.v3_7 import importlib_metadata as metadata
325
- elif _py_ver >= (3, 6):
326
- from metaflow._vendor.v3_6 import importlib_metadata as metadata
327
- else:
328
- from metaflow._vendor.v3_5 import importlib_metadata as metadata
329
- _mfext_supported = True
328
+ if _py_ver >= (3, 8):
329
+ from importlib import metadata
330
+ elif _py_ver >= (3, 7):
331
+ from metaflow._vendor.v3_7 import importlib_metadata as metadata
332
+ else:
333
+ from metaflow._vendor.v3_6 import importlib_metadata as metadata
330
334
 
331
335
  # Extension points are the directories that can be present in a EXT_PKG to
332
336
  # contribute to that extension point. For example, if you have
@@ -355,10 +359,6 @@ def _ext_debug(*args, **kwargs):
355
359
 
356
360
 
357
361
  def _get_extension_packages(ignore_info_file=False, restrict_to_directories=None):
358
- if not _mfext_supported:
359
- _ext_debug("Not supported for your Python version -- 3.4+ is needed")
360
- return {}, {}
361
-
362
362
  # If we have an INFO file with the appropriate information (if running from a saved
363
363
  # code package for example), we use that directly
364
364
  # Pre-compute on _extension_points
@@ -381,12 +381,11 @@ def _get_extension_packages(ignore_info_file=False, restrict_to_directories=None
381
381
  try:
382
382
  extensions_module = importlib.import_module(EXT_PKG)
383
383
  except ImportError as e:
384
- if _py_ver >= (3, 6):
385
- # e.name is set to the name of the package that fails to load
386
- # so don't error ONLY IF the error is importing this module (but do
387
- # error if there is a transitive import error)
388
- if not (isinstance(e, ModuleNotFoundError) and e.name == EXT_PKG):
389
- raise
384
+ # e.name is set to the name of the package that fails to load
385
+ # so don't error ONLY IF the error is importing this module (but do
386
+ # error if there is a transitive import error)
387
+ if not (isinstance(e, ModuleNotFoundError) and e.name == EXT_PKG):
388
+ raise
390
389
  return {}, {}
391
390
 
392
391
  if restrict_to_directories:
@@ -406,7 +405,80 @@ def _get_extension_packages(ignore_info_file=False, restrict_to_directories=None
406
405
  # At this point, we look at all the paths and create a set. As we find distributions
407
406
  # that match it, we will remove from the set and then will be left with any
408
407
  # PYTHONPATH "packages"
409
- all_paths = set(Path(p).resolve().as_posix() for p in extensions_module.__path__)
408
+ all_paths = set()
409
+ # Records which finders provided which paths if applicable
410
+ # This is then later used to determine which paths belong
411
+ # to which distribution
412
+ finders_to_paths = dict()
413
+
414
+ # Temporary variables to support the loop below and make sure we loop through all
415
+ # the paths in the submodule_search_locations including calling the path hooks.
416
+ # We could skip calling things on the path hooks since the module was just imported
417
+ # by importlib so the values are probably already in submodule_search_locations but
418
+ # there may be cases where we need to call multiple times. This also allows us to tie
419
+ # the finders (ie: the path hooks) back to the distribution since they share a name.
420
+ # This is useful in knowing which paths we consider as belonging to a distribution so
421
+ # we know which order to load it in.
422
+ seen_path_values = set()
423
+ new_paths = extensions_module.__spec__.submodule_search_locations
424
+ _ext_debug("Found initial paths: %s" % str(new_paths))
425
+ while new_paths:
426
+ paths = new_paths
427
+ new_paths = []
428
+ for p in paths:
429
+ if p in seen_path_values:
430
+ continue
431
+ if os.path.isdir(p):
432
+ all_paths.add(Path(p).resolve().as_posix())
433
+ elif p in sys.path_importer_cache:
434
+ # We have a path hook that we likely need to call to get the actual path
435
+ addl_spec = sys.path_importer_cache[p].find_spec(EXT_PKG)
436
+ if addl_spec is not None and addl_spec.submodule_search_locations:
437
+ new_paths.extend(addl_spec.submodule_search_locations)
438
+ # Remove .__path_hook__ and add .py to match the name of the file
439
+ # installed by the distribution
440
+ finder_name = p[:-14].translate(FINDER_TRANS) + ".py"
441
+ new_dirs = [
442
+ d
443
+ for d in addl_spec.submodule_search_locations
444
+ if os.path.isdir(d)
445
+ ]
446
+ _ext_debug(
447
+ "Finder %s added directories %s"
448
+ % (finder_name, ", ".join(new_dirs))
449
+ )
450
+ finders_to_paths.setdefault(finder_name, []).extend(new_dirs)
451
+ else:
452
+ # This may not be as required since it is likely the importer cache has
453
+ # everything already but just in case, we will also go through the
454
+ # path hooks and see if we find another one
455
+ for path_hook in sys.path_hooks:
456
+ try:
457
+ finder = path_hook(p)
458
+ addl_spec = finder.find_spec(EXT_PKG)
459
+ if (
460
+ addl_spec is not None
461
+ and addl_spec.submodule_search_locations
462
+ ):
463
+ finder_name = p[:-14].translate(FINDER_TRANS) + ".py"
464
+ new_dirs = [
465
+ d
466
+ for d in addl_spec.submodule_search_locations
467
+ if os.path.isdir(d)
468
+ ]
469
+ _ext_debug(
470
+ "Finder (through hooks) %s added directories %s"
471
+ % (finder_name, ", ".join(new_dirs))
472
+ )
473
+ finders_to_paths.setdefault(finder_name, []).extend(
474
+ new_dirs
475
+ )
476
+ new_paths.extend(addl_spec.submodule_search_locations)
477
+ break
478
+ except ImportError:
479
+ continue
480
+ seen_path_values.add(p)
481
+
410
482
  _ext_debug("Found packages present at %s" % str(all_paths))
411
483
  if restrict_to_directories:
412
484
  _ext_debug(
@@ -448,18 +520,146 @@ def _get_extension_packages(ignore_info_file=False, restrict_to_directories=None
448
520
  # Same as config_to_pkg for meta files
449
521
  meta_to_pkg = defaultdict(list)
450
522
 
523
+ # The file passed to process_file has EXT_PKG as the first component
524
+ # root_dir also has EXT_PKG as the last component
525
+ def process_file(state: Dict[str, Any], root_dir: str, file: str):
526
+ parts = file.split("/")
527
+
528
+ if len(parts) > 1 and parts[0] == EXT_PKG:
529
+ # Check for top-level files (ie: meta file which specifies how to package
530
+ # the extension and __init__.py file)
531
+ if len(parts) == 2:
532
+ # Ensure that we don't have a __init__.py to force this package to
533
+ # be a NS package
534
+ if parts[1] == "__init__.py":
535
+ raise RuntimeError(
536
+ "Package '%s' providing '%s' is not an implicit namespace "
537
+ "package as required" % (state["name"], EXT_PKG)
538
+ )
539
+ # Check for any metadata; we can only have one metadata per
540
+ # distribution at most
541
+ if EXT_META_REGEXP.match(parts[1]) is not None:
542
+ potential_meta_module = ".".join([EXT_PKG, parts[1][:-3]])
543
+ if state["meta_module"]:
544
+ raise RuntimeError(
545
+ "Package '%s' defines more than one meta configuration: "
546
+ "'%s' and '%s' (at least)"
547
+ % (
548
+ state["name"],
549
+ state["meta_module"],
550
+ potential_meta_module,
551
+ )
552
+ )
553
+ state["meta_module"] = potential_meta_module
554
+ _ext_debug(
555
+ "Found meta '%s' for '%s'"
556
+ % (state["meta_module"], state["name"])
557
+ )
558
+ meta_to_pkg[state["meta_module"]].append(state["name"])
559
+
560
+ # Record the file as a candidate for inclusion when packaging if
561
+ # needed
562
+ if not any(parts[-1].endswith(suffix) for suffix in EXT_EXCLUDE_SUFFIXES):
563
+ # Strip out metaflow_extensions from the file
564
+ state["files"].append(os.path.join(*parts[1:]))
565
+ state["full_path_files"].append(os.path.join(root_dir, *parts[1:]))
566
+
567
+ if parts[1] in init_ext_points:
568
+ # This is most likely a problem as we need an intermediate
569
+ # "identifier"
570
+ raise RuntimeError(
571
+ "Package '%s' should conform to '%s.X.%s' and not '%s.%s' where "
572
+ "X is your organization's name for example"
573
+ % (
574
+ state["name"],
575
+ EXT_PKG,
576
+ parts[1],
577
+ EXT_PKG,
578
+ parts[1],
579
+ )
580
+ )
581
+
582
+ if len(parts) > 3 and parts[0] == EXT_PKG:
583
+ # We go over _extension_points *in order* to make sure we get more
584
+ # specific paths first
585
+
586
+ # To give useful errors in case multiple top-level packages in
587
+ # one package
588
+ dist_full_name = "%s[%s]" % (state["name"], parts[1])
589
+ for idx, ext_list in enumerate(list_ext_points):
590
+ if (
591
+ len(parts) > len(ext_list) + 2
592
+ and parts[2 : 2 + len(ext_list)] == ext_list
593
+ ):
594
+ # Check if this is an "init" file
595
+ config_module = None
596
+
597
+ if len(parts) == len(ext_list) + 3 and (
598
+ EXT_CONFIG_REGEXP.match(parts[-1]) is not None
599
+ or parts[-1] == "__init__.py"
600
+ ):
601
+ parts[-1] = parts[-1][:-3] # Remove the .py
602
+ config_module = ".".join(parts)
603
+
604
+ config_to_pkg[config_module].append(dist_full_name)
605
+ cur_pkg = (
606
+ extension_points_to_pkg[_extension_points[idx]]
607
+ .setdefault(state["name"], {})
608
+ .get(parts[1])
609
+ )
610
+ if cur_pkg is not None:
611
+ if (
612
+ config_module is not None
613
+ and cur_pkg.config_module is not None
614
+ ):
615
+ raise RuntimeError(
616
+ "Package '%s' defines more than one "
617
+ "configuration file for '%s': '%s' and '%s'"
618
+ % (
619
+ dist_full_name,
620
+ _extension_points[idx],
621
+ config_module,
622
+ cur_pkg.config_module,
623
+ )
624
+ )
625
+ if config_module is not None:
626
+ _ext_debug(
627
+ " Top-level '%s' found config file '%s'"
628
+ % (parts[1], config_module)
629
+ )
630
+ extension_points_to_pkg[_extension_points[idx]][
631
+ state["name"]
632
+ ][parts[1]] = MFExtPackage(
633
+ package_name=state["name"],
634
+ tl_package=parts[1],
635
+ config_module=config_module,
636
+ )
637
+ else:
638
+ _ext_debug(
639
+ " Top-level '%s' extends '%s' with config '%s'"
640
+ % (parts[1], _extension_points[idx], config_module)
641
+ )
642
+ extension_points_to_pkg[_extension_points[idx]][state["name"]][
643
+ parts[1]
644
+ ] = MFExtPackage(
645
+ package_name=state["name"],
646
+ tl_package=parts[1],
647
+ config_module=config_module,
648
+ )
649
+ break
650
+
451
651
  # 1st step: look for distributions (the common case)
452
652
  for dist in metadata.distributions():
453
653
  if any(
454
654
  [pkg == EXT_PKG for pkg in (dist.read_text("top_level.txt") or "").split()]
455
655
  ):
456
- # In all cases (whether duplicate package or not), we remove the package
457
- # from the list of locations to look in.
458
- # This is not 100% accurate because it is possible that at the same
459
- # location there is a package and a non-package, but this is extremely
460
- # unlikely so we are going to ignore this case.
656
+ # Note that locate_file does not actually make sure the file exists. It just
657
+ # appends whatever you pass in to locate_file to the folder containing the
658
+ # metadata for the distribution. We will therefore check if we are actually
659
+ # seeing files in that directory using has_file_in_dist_root.
461
660
  dist_root = dist.locate_file(EXT_PKG).resolve().as_posix()
462
- all_paths.discard(dist_root)
661
+ all_roots = []
662
+ has_file_in_dist_root = False
463
663
  dist_name = dist.metadata["Name"]
464
664
  dist_version = dist.metadata["Version"]
465
665
  if restrict_to_directories:
@@ -479,144 +679,88 @@ def _get_extension_packages(ignore_info_file=False, restrict_to_directories=None
479
679
  )
480
680
  continue
481
681
  _ext_debug(
482
- "Found extension package '%s' at '%s'..." % (dist_name, dist_root)
682
+ "Found extension package '%s' at presumptive path '%s'..."
683
+ % (dist_name, dist_root)
483
684
  )
484
685
 
485
- files_to_include = []
486
- meta_module = None
487
-
686
+ state = {
687
+ "name": dist_name,
688
+ "files": [],
689
+ "full_path_files": [],
690
+ "meta_module": None, # Meta information about the package (if applicable)
691
+ }
692
+ addl_dirs = []
488
693
  # At this point, we check to see what extension points this package
489
694
  # contributes to. This is to enable multiple namespace packages to contribute
490
695
  # to the same extension point (for example, you may have multiple packages
491
696
  # that have plugins)
492
- for f in dist.files:
493
- parts = list(f.parts)
494
-
495
- if len(parts) > 1 and parts[0] == EXT_PKG:
496
- # Ensure that we don't have a __init__.py to force this package to
497
- # be a NS package
498
- if parts[1] == "__init__.py":
499
- raise RuntimeError(
500
- "Package '%s' providing '%s' is not an implicit namespace "
501
- "package as required" % (dist_name, EXT_PKG)
502
- )
503
-
504
- # Record the file as a candidate for inclusion when packaging if
505
- # needed
506
- if not any(
507
- parts[-1].endswith(suffix) for suffix in EXT_EXCLUDE_SUFFIXES
508
- ):
509
- files_to_include.append(os.path.join(*parts[1:]))
510
-
511
- if parts[1] in init_ext_points:
512
- # This is most likely a problem as we need an intermediate
513
- # "identifier"
514
- raise RuntimeError(
515
- "Package '%s' should conform to '%s.X.%s' and not '%s.%s' where "
516
- "X is your organization's name for example"
517
- % (
518
- dist_name,
519
- EXT_PKG,
520
- parts[1],
521
- EXT_PKG,
522
- parts[1],
523
- )
524
- )
697
+ for f in dist.files or []:
698
+ if f.suffix == ".pth":
699
+ # This is a directory we need to walk to find the files
700
+ d = f.read_text().strip()
701
+ if os.path.isdir(d):
702
+ _ext_debug(" Found additional directory '%s' from .pth" % d)
703
+ addl_dirs.append(d)
704
+ elif str(f).startswith("__editable__"):
705
+ # This is a finder file because we already checked for .pth
706
+ _ext_debug(
707
+ " Added additional directories from finder '%s': %s"
708
+ % (str(f), ", ".join(finders_to_paths.get(str(f), [])))
709
+ )
710
+ addl_dirs.extend(finders_to_paths.get(str(f), []))
711
+ elif f.parts[0] == EXT_PKG:
712
+ has_file_in_dist_root = True
713
+ process_file(state, dist_root, str(f))
714
+ else:
715
+ # We ignore the file
716
+ continue
525
717
 
526
- # Check for any metadata; we can only have one metadata per
527
- # distribution at most
528
- if EXT_META_REGEXP.match(parts[1]) is not None:
529
- potential_meta_module = ".".join([EXT_PKG, parts[1][:-3]])
530
- if meta_module:
531
- raise RuntimeError(
532
- "Package '%s' defines more than one meta configuration: "
533
- "'%s' and '%s' (at least)"
534
- % (
535
- dist_name,
536
- meta_module,
537
- potential_meta_module,
538
- )
539
- )
540
- meta_module = potential_meta_module
718
+ if has_file_in_dist_root:
719
+ all_roots.append(dist_root)
720
+ all_paths.discard(dist_root)
721
+ # Now walk any additional directory for this distribution as well
722
+ for addl_dir in addl_dirs:
723
+ if restrict_to_directories:
724
+ parent_dirs = list(
725
+ p.as_posix() for p in Path(addl_dir).resolve().parents
726
+ )
727
+ if all(p not in parent_dirs for p in restrict_to_directories):
541
728
  _ext_debug(
542
- "Found meta '%s' for '%s'" % (meta_module, dist_full_name)
729
+ "Ignoring package at %s as it is not in the considered "
730
+ "directories" % addl_dir
543
731
  )
544
- meta_to_pkg[meta_module].append(dist_full_name)
545
-
546
- if len(parts) > 3 and parts[0] == EXT_PKG:
547
- # We go over _extension_points *in order* to make sure we get more
548
- # specific paths first
549
-
550
- # To give useful errors in case multiple top-level packages in
551
- # one package
552
- dist_full_name = "%s[%s]" % (dist_name, parts[1])
553
- for idx, ext_list in enumerate(list_ext_points):
554
- if (
555
- len(parts) > len(ext_list) + 2
556
- and parts[2 : 2 + len(ext_list)] == ext_list
557
- ):
558
- # Check if this is an "init" file
559
- config_module = None
560
-
561
- if len(parts) == len(ext_list) + 3 and (
562
- EXT_CONFIG_REGEXP.match(parts[-1]) is not None
563
- or parts[-1] == "__init__.py"
564
- ):
565
- parts[-1] = parts[-1][:-3] # Remove the .py
566
- config_module = ".".join(parts)
567
-
568
- config_to_pkg[config_module].append(dist_full_name)
569
- cur_pkg = (
570
- extension_points_to_pkg[_extension_points[idx]]
571
- .setdefault(dist_name, {})
572
- .get(parts[1])
573
- )
574
- if cur_pkg is not None:
575
- if (
576
- config_module is not None
577
- and cur_pkg.config_module is not None
578
- ):
579
- raise RuntimeError(
580
- "Package '%s' defines more than one "
581
- "configuration file for '%s': '%s' and '%s'"
582
- % (
583
- dist_full_name,
584
- _extension_points[idx],
585
- config_module,
586
- cur_pkg.config_module,
587
- )
588
- )
589
- if config_module is not None:
590
- _ext_debug(
591
- " Top-level '%s' found config file '%s'"
592
- % (parts[1], config_module)
593
- )
594
- extension_points_to_pkg[_extension_points[idx]][
595
- dist_name
596
- ][parts[1]] = MFExtPackage(
597
- package_name=dist_name,
598
- tl_package=parts[1],
599
- config_module=config_module,
600
- )
601
- else:
602
- _ext_debug(
603
- " Top-level '%s' extends '%s' with config '%s'"
604
- % (parts[1], _extension_points[idx], config_module)
605
- )
606
- extension_points_to_pkg[_extension_points[idx]][
607
- dist_name
608
- ][parts[1]] = MFExtPackage(
609
- package_name=dist_name,
610
- tl_package=parts[1],
611
- config_module=config_module,
612
- )
613
- break
732
+ continue
733
+ base_depth = len(addl_dir.split("/"))
734
+ # .pth files give addl_dirs that don't have EXT_PKG at the end but
735
+ # finders do so check this
736
+ if addl_dir.split("/")[-1] == EXT_PKG:
737
+ base_depth -= 1
738
+ else:
739
+ addl_dir = os.path.join(addl_dir, EXT_PKG)
740
+ all_roots.append(addl_dir)
741
+ all_paths.discard(addl_dir)
742
+ _ext_debug(" Walking additional directory '%s'" % addl_dir)
743
+ for root, _, files in walk_without_cycles(addl_dir):
744
+ relative_root = "/".join(root.split("/")[base_depth:])
745
+ for f in files:
746
+ process_file(state, addl_dir, os.path.join(relative_root, f))
614
747
  mf_ext_packages[dist_name] = {
615
- "root_paths": [dist_root],
616
- "meta_module": meta_module,
617
- "files": files_to_include,
748
+ "root_paths": all_roots,
749
+ "meta_module": state["meta_module"],
750
+ "full_path_files": state["full_path_files"],
751
+ "files": state["files"],
618
752
  "version": dist_version,
619
753
  }
754
+ if addl_dirs:
755
+ # If we have additional directories, this means that we may need to filter
756
+ # the files based on the meta information about the module since we
757
+ # walked down the directories instead of relying simply on files that
758
+ # were packaged with the distribution. We do this now so we don't have to
759
+ # do it multiple times later for packaging. This is only useful if the
760
+ # distribution does not completely specify the files that need to be
761
+ # installed. In the case where the distribution completely specifies the
762
+ # files, we ignore the meta module
763
+ _filter_files_package(mf_ext_packages[dist_name])
620
764
  # At this point, we have all the packages that contribute to EXT_PKG,
621
765
  # we now check to see if there is an order to respect based on dependencies. We will
622
766
  # return an ordered list that respects that order and is ordered alphabetically in
@@ -677,9 +821,7 @@ def _get_extension_packages(ignore_info_file=False, restrict_to_directories=None
677
821
  all_paths_list.sort()
678
822
 
679
823
  # This block of code is the equivalent of the one above for distributions except
680
- # for PYTHONPATH packages. The functionality is identical, but it looks a little
681
- # different because we construct the file list instead of having it nicely provided
682
- # to us.
824
+ # for PYTHONPATH packages.
683
825
  package_name_to_path = dict()
684
826
  if len(all_paths_list) > 0:
685
827
  _ext_debug("Non installed packages present at %s" % str(all_paths))
@@ -706,132 +848,32 @@ def _get_extension_packages(ignore_info_file=False, restrict_to_directories=None
706
848
  )
707
849
  package_name_to_path[package_name] = package_path
708
850
  base_depth = len(package_path.split("/"))
709
- files_to_include = []
710
- meta_module = None
711
- for root, dirs, files in os.walk(package_path):
712
- parts = root.split("/")
713
- cur_depth = len(parts)
714
- # relative_root strips out metaflow_extensions
715
- relative_root = "/".join(parts[base_depth:])
716
- relative_module = ".".join(parts[base_depth - 1 :])
717
- files_to_include.extend(
718
- [
719
- "/".join([relative_root, f]) if relative_root else f
720
- for f in files
721
- if not any(
722
- [f.endswith(suffix) for suffix in EXT_EXCLUDE_SUFFIXES]
723
- )
724
- ]
725
- )
726
- if cur_depth == base_depth:
727
- if "__init__.py" in files:
728
- raise RuntimeError(
729
- "'%s' at '%s' is not an implicit namespace package as required"
730
- % (EXT_PKG, root)
731
- )
732
- for d in dirs:
733
- if d in init_ext_points:
734
- raise RuntimeError(
735
- "Package at '%s' should conform to' %s.X.%s' and not "
736
- "'%s.%s' where X is your organization's name for example"
737
- % (root, EXT_PKG, d, EXT_PKG, d)
738
- )
739
- # Check for meta files for this package
740
- meta_files = [
741
- x for x in map(EXT_META_REGEXP.match, files) if x is not None
742
- ]
743
- if meta_files:
744
- # We should have one meta file at most
745
- if len(meta_files) > 1:
746
- raise RuntimeError(
747
- "Package at '%s' defines more than one meta file: %s"
748
- % (
749
- package_path,
750
- ", and ".join(
751
- ["'%s'" % x.group(0) for x in meta_files]
752
- ),
753
- )
754
- )
755
- else:
756
- meta_module = ".".join(
757
- [relative_module, meta_files[0].group(0)[:-3]]
758
- )
759
-
760
- elif cur_depth > base_depth + 1:
761
- # We want at least a top-level name and something under
762
- tl_name = parts[base_depth]
763
- tl_fullname = "%s[%s]" % (package_path, tl_name)
764
- prefix_match = parts[base_depth + 1 :]
765
- for idx, ext_list in enumerate(list_ext_points):
766
- if prefix_match == ext_list:
767
- # We check to see if this is an actual extension point
768
- # or if we just have a directory on the way to another
769
- # extension point. To do this, we check to see if we have
770
- # any files or directories that are *not* directly another
771
- # extension point
772
- skip_extension = len(files) == 0
773
- if skip_extension:
774
- next_dir_idx = len(list_ext_points[idx])
775
- ok_subdirs = [
776
- list_ext_points[j][next_dir_idx]
777
- for j in range(0, idx)
778
- if len(list_ext_points[j]) > next_dir_idx
779
- ]
780
- skip_extension = set(dirs).issubset(set(ok_subdirs))
781
-
782
- if skip_extension:
783
- _ext_debug(
784
- " Skipping '%s' as no files/directory of interest"
785
- % _extension_points[idx]
786
- )
787
- continue
788
-
789
- # Check for any "init" files
790
- init_files = [
791
- x.group(0)
792
- for x in map(EXT_CONFIG_REGEXP.match, files)
793
- if x is not None
794
- ]
795
- if "__init__.py" in files:
796
- init_files.append("__init__.py")
797
-
798
- config_module = None
799
- if len(init_files) > 1:
800
- raise RuntimeError(
801
- "Package at '%s' defines more than one configuration "
802
- "file for '%s': %s"
803
- % (
804
- tl_fullname,
805
- ".".join(prefix_match),
806
- ", and ".join(["'%s'" % x for x in init_files]),
807
- )
808
- )
809
- elif len(init_files) == 1:
810
- config_module = ".".join(
811
- [relative_module, init_files[0][:-3]]
812
- )
813
- config_to_pkg[config_module].append(tl_fullname)
814
-
815
- d = extension_points_to_pkg[_extension_points[idx]][
816
- package_name
817
- ] = dict()
818
- d[tl_name] = MFExtPackage(
819
- package_name=package_name,
820
- tl_package=tl_name,
821
- config_module=config_module,
822
- )
823
- _ext_debug(
824
- " Extends '%s' with config '%s'"
825
- % (_extension_points[idx], config_module)
826
- )
827
- mf_pkg_list.append(package_name)
828
- mf_ext_packages[package_name] = {
829
- "root_paths": [package_path],
830
- "meta_module": meta_module,
831
- "files": files_to_include,
832
- "version": "_local_",
851
+ state = {
852
+ "name": package_name,
853
+ "files": [],
854
+ "full_path_files": [],
855
+ "meta_module": None,
833
856
  }
834
857
 
858
+ for root, _, files in walk_without_cycles(package_path):
859
+ relative_root = "/".join(root.split("/")[base_depth - 1 :])
860
+ for f in files:
861
+ process_file(state, package_path, os.path.join(relative_root, f))
862
+
863
+ if state["files"]:
864
+ mf_pkg_list.append(package_name)
865
+ mf_ext_packages[package_name] = {
866
+ "root_paths": [package_path],
867
+ "meta_module": state["meta_module"],
868
+ "full_path_files": state["full_path_files"],
869
+ "files": state["files"],
870
+ "version": "_local_",
871
+ }
872
+ # Always filter here since we don't have any distribution information
873
+ _filter_files_package(mf_ext_packages[package_name])
874
+ else:
875
+ _ext_debug("Skipping package as no files found (empty dir?)")
876
+
835
877
  # Sanity check that we only have one package per configuration file.
836
878
  # This prevents multiple packages from providing the same named configuration
837
879
  # file which would result in one overwriting the other if they are both installed.
@@ -887,33 +929,83 @@ def _get_extension_packages(ignore_info_file=False, restrict_to_directories=None
887
929
  return mf_ext_packages, extension_points_to_pkg
888
930
 
889
931
 
890
- _all_packages, _pkgs_per_extension_point = _get_extension_packages()
891
-
892
-
893
932
  def _attempt_load_module(module_name):
894
933
  try:
895
934
  extension_module = importlib.import_module(module_name)
896
935
  except ImportError as e:
897
- if _py_ver >= (3, 6):
898
- # e.name is set to the name of the package that fails to load
899
- # so don't error ONLY IF the error is importing this module (but do
900
- # error if there is a transitive import error)
901
- errored_names = [EXT_PKG]
902
- parts = module_name.split(".")
903
- for p in parts[1:]:
904
- errored_names.append("%s.%s" % (errored_names[-1], p))
905
- if not (isinstance(e, ModuleNotFoundError) and e.name in errored_names):
906
- print(
907
- "The following exception occurred while trying to load '%s' ('%s')"
908
- % (EXT_PKG, module_name)
909
- )
910
- raise
936
+ # e.name is set to the name of the package that fails to load
937
+ # so don't error ONLY IF the error is importing this module (but do
938
+ # error if there is a transitive import error)
939
+ errored_names = [EXT_PKG]
940
+ parts = module_name.split(".")
941
+ for p in parts[1:]:
942
+ errored_names.append("%s.%s" % (errored_names[-1], p))
943
+ if not (isinstance(e, ModuleNotFoundError) and e.name in errored_names):
944
+ print(
945
+ "The following exception occurred while trying to load '%s' ('%s')"
946
+ % (EXT_PKG, module_name)
947
+ )
948
+ raise
911
949
  _ext_debug(" Unknown error when loading '%s': %s" % (module_name, e))
912
950
  return None
913
951
  else:
914
952
  return extension_module
915
953
 
916
954
 
955
+ def _filter_files_package(pkg):
956
+ if pkg and pkg["root_paths"] and pkg["meta_module"]:
957
+ meta_module = _attempt_load_module(pkg["meta_module"])
958
+ if meta_module:
959
+ filter_function = meta_module.__dict__.get("filter_function")
960
+ include_suffixes = meta_module.__dict__.get("include_suffixes")
961
+ exclude_suffixes = meta_module.__dict__.get("exclude_suffixes")
962
+
963
+ # Behavior is as follows:
964
+ # - if nothing specified, include all files (so do nothing here)
965
+ # - if filter_function specified, call that function on the list of files
966
+ # and only include the files where the function returns True. Note that
967
+ # the function will always be passed a value that starts with
968
+ # metaflow_extensions/...
969
+ # - if include_suffixes, only include those suffixes
970
+ # - if *not* include_suffixes but exclude_suffixes, include everything *except*
971
+ # files ending with that suffix
972
+ new_files, new_full_path_files = [], []
973
+
974
+ if filter_function:
975
+ for short_file, full_file in zip(pkg["files"], pkg["full_path_files"]):
976
+ try:
977
+ if filter_function(os.path.join(EXT_PKG, short_file)):
978
+ new_files.append(short_file)
979
+ new_full_path_files.append(full_file)
980
+ except Exception as e:
981
+ _ext_debug(
982
+ " Exception '%s' when calling filter_function on "
983
+ "'%s', ignoring file" % (e, short_file)
984
+ )
985
+ elif include_suffixes:
986
+ for short_file, full_file in zip(pkg["files"], pkg["full_path_files"]):
987
+ if any(
988
+ [short_file.endswith(suffix) for suffix in include_suffixes]
989
+ ):
990
+ new_files.append(short_file)
991
+ new_full_path_files.append(full_file)
992
+ elif exclude_suffixes:
993
+ for short_file, full_file in zip(pkg["files"], pkg["full_path_files"]):
994
+ if not any(
995
+ [short_file.endswith(suffix) for suffix in exclude_suffixes]
996
+ ):
997
+ new_files.append(short_file)
998
+ new_full_path_files.append(full_file)
999
+ else:
1000
+ new_files = pkg["files"]
1001
+ new_full_path_files = pkg["full_path_files"]
1002
+ pkg["files"] = new_files
1003
+ pkg["full_path_files"] = new_full_path_files
1004
+
1005
+
1006
+ _all_packages, _pkgs_per_extension_point = _get_extension_packages()
1007
+
1008
+
917
1009
  def _get_extension_config(distribution_name, tl_pkg, extension_point, config_module):
918
1010
  if config_module is not None and not config_module.endswith("__init__"):
919
1011
  module_name = config_module
@@ -964,40 +1056,6 @@ def _get_extension_config(distribution_name, tl_pkg, extension_point, config_mod
964
1056
  return None
965
1057
 
966
1058
 
967
- def _filter_files_package(pkg):
968
- if pkg and pkg["root_paths"] and pkg["meta_module"]:
969
- meta_module = _attempt_load_module(pkg["meta_module"])
970
- if meta_module:
971
- include_suffixes = meta_module.__dict__.get("include_suffixes")
972
- exclude_suffixes = meta_module.__dict__.get("exclude_suffixes")
973
-
974
- # Behavior is as follows:
975
- # - if nothing specified, include all files (so do nothing here)
976
- # - if include_suffixes, only include those suffixes
977
- # - if *not* include_suffixes but exclude_suffixes, include everything *except*
978
- # files ending with that suffix
979
- if include_suffixes:
980
- new_files = [
981
- f
982
- for f in pkg["files"]
983
- if any([f.endswith(suffix) for suffix in include_suffixes])
984
- ]
985
- elif exclude_suffixes:
986
- new_files = [
987
- f
988
- for f in pkg["files"]
989
- if not any([f.endswith(suffix) for suffix in exclude_suffixes])
990
- ]
991
- else:
992
- new_files = pkg["files"]
993
- pkg["files"] = new_files
994
-
995
-
996
- def _filter_files_all(all_packages):
997
- for p in all_packages.values():
998
- _filter_files_package(p)
999
-
1000
-
1001
1059
  class _AliasLoader(Loader):
1002
1060
  def __init__(self, alias, orig):
1003
1061
  self._alias = alias