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.
- metaflow/__init__.py +10 -3
- metaflow/_vendor/imghdr/__init__.py +186 -0
- metaflow/_vendor/yaml/__init__.py +427 -0
- metaflow/_vendor/yaml/composer.py +139 -0
- metaflow/_vendor/yaml/constructor.py +748 -0
- metaflow/_vendor/yaml/cyaml.py +101 -0
- metaflow/_vendor/yaml/dumper.py +62 -0
- metaflow/_vendor/yaml/emitter.py +1137 -0
- metaflow/_vendor/yaml/error.py +75 -0
- metaflow/_vendor/yaml/events.py +86 -0
- metaflow/_vendor/yaml/loader.py +63 -0
- metaflow/_vendor/yaml/nodes.py +49 -0
- metaflow/_vendor/yaml/parser.py +589 -0
- metaflow/_vendor/yaml/reader.py +185 -0
- metaflow/_vendor/yaml/representer.py +389 -0
- metaflow/_vendor/yaml/resolver.py +227 -0
- metaflow/_vendor/yaml/scanner.py +1435 -0
- metaflow/_vendor/yaml/serializer.py +111 -0
- metaflow/_vendor/yaml/tokens.py +104 -0
- metaflow/cards.py +4 -0
- metaflow/cli.py +125 -21
- metaflow/cli_components/init_cmd.py +1 -0
- metaflow/cli_components/run_cmds.py +204 -40
- metaflow/cli_components/step_cmd.py +160 -4
- metaflow/client/__init__.py +1 -0
- metaflow/client/core.py +198 -130
- metaflow/client/filecache.py +59 -32
- metaflow/cmd/code/__init__.py +2 -1
- metaflow/cmd/develop/stub_generator.py +49 -18
- metaflow/cmd/develop/stubs.py +9 -27
- metaflow/cmd/make_wrapper.py +30 -0
- metaflow/datastore/__init__.py +1 -0
- metaflow/datastore/content_addressed_store.py +40 -9
- metaflow/datastore/datastore_set.py +10 -1
- metaflow/datastore/flow_datastore.py +124 -4
- metaflow/datastore/spin_datastore.py +91 -0
- metaflow/datastore/task_datastore.py +92 -6
- metaflow/debug.py +5 -0
- metaflow/decorators.py +331 -82
- metaflow/extension_support/__init__.py +414 -356
- metaflow/extension_support/_empty_file.py +2 -2
- metaflow/flowspec.py +322 -82
- metaflow/graph.py +178 -15
- metaflow/includefile.py +25 -3
- metaflow/lint.py +94 -3
- metaflow/meta_files.py +13 -0
- metaflow/metadata_provider/metadata.py +13 -2
- metaflow/metaflow_config.py +66 -4
- metaflow/metaflow_environment.py +91 -25
- metaflow/metaflow_profile.py +18 -0
- metaflow/metaflow_version.py +16 -1
- metaflow/package/__init__.py +673 -0
- metaflow/packaging_sys/__init__.py +880 -0
- metaflow/packaging_sys/backend.py +128 -0
- metaflow/packaging_sys/distribution_support.py +153 -0
- metaflow/packaging_sys/tar_backend.py +99 -0
- metaflow/packaging_sys/utils.py +54 -0
- metaflow/packaging_sys/v1.py +527 -0
- metaflow/parameters.py +6 -2
- metaflow/plugins/__init__.py +6 -0
- metaflow/plugins/airflow/airflow.py +11 -1
- metaflow/plugins/airflow/airflow_cli.py +16 -5
- metaflow/plugins/argo/argo_client.py +42 -20
- metaflow/plugins/argo/argo_events.py +6 -6
- metaflow/plugins/argo/argo_workflows.py +1023 -344
- metaflow/plugins/argo/argo_workflows_cli.py +396 -94
- metaflow/plugins/argo/argo_workflows_decorator.py +9 -0
- metaflow/plugins/argo/argo_workflows_deployer_objects.py +75 -49
- metaflow/plugins/argo/capture_error.py +5 -2
- metaflow/plugins/argo/conditional_input_paths.py +35 -0
- metaflow/plugins/argo/exit_hooks.py +209 -0
- metaflow/plugins/argo/param_val.py +19 -0
- metaflow/plugins/aws/aws_client.py +6 -0
- metaflow/plugins/aws/aws_utils.py +33 -1
- metaflow/plugins/aws/batch/batch.py +72 -5
- metaflow/plugins/aws/batch/batch_cli.py +24 -3
- metaflow/plugins/aws/batch/batch_decorator.py +57 -6
- metaflow/plugins/aws/step_functions/step_functions.py +28 -3
- metaflow/plugins/aws/step_functions/step_functions_cli.py +49 -4
- metaflow/plugins/aws/step_functions/step_functions_deployer.py +3 -0
- metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +30 -0
- metaflow/plugins/cards/card_cli.py +20 -1
- metaflow/plugins/cards/card_creator.py +24 -1
- metaflow/plugins/cards/card_datastore.py +21 -49
- metaflow/plugins/cards/card_decorator.py +58 -6
- metaflow/plugins/cards/card_modules/basic.py +38 -9
- metaflow/plugins/cards/card_modules/bundle.css +1 -1
- metaflow/plugins/cards/card_modules/chevron/renderer.py +1 -1
- metaflow/plugins/cards/card_modules/components.py +592 -3
- metaflow/plugins/cards/card_modules/convert_to_native_type.py +34 -5
- metaflow/plugins/cards/card_modules/json_viewer.py +232 -0
- metaflow/plugins/cards/card_modules/main.css +1 -0
- metaflow/plugins/cards/card_modules/main.js +56 -41
- metaflow/plugins/cards/card_modules/test_cards.py +22 -6
- metaflow/plugins/cards/component_serializer.py +1 -8
- metaflow/plugins/cards/metadata.py +22 -0
- metaflow/plugins/catch_decorator.py +9 -0
- metaflow/plugins/datastores/local_storage.py +12 -6
- metaflow/plugins/datastores/spin_storage.py +12 -0
- metaflow/plugins/datatools/s3/s3.py +49 -17
- metaflow/plugins/datatools/s3/s3op.py +113 -66
- metaflow/plugins/env_escape/client_modules.py +102 -72
- metaflow/plugins/events_decorator.py +127 -121
- metaflow/plugins/exit_hook/__init__.py +0 -0
- metaflow/plugins/exit_hook/exit_hook_decorator.py +46 -0
- metaflow/plugins/exit_hook/exit_hook_script.py +52 -0
- metaflow/plugins/kubernetes/kubernetes.py +12 -1
- metaflow/plugins/kubernetes/kubernetes_cli.py +11 -0
- metaflow/plugins/kubernetes/kubernetes_decorator.py +25 -6
- metaflow/plugins/kubernetes/kubernetes_job.py +12 -4
- metaflow/plugins/kubernetes/kubernetes_jobsets.py +31 -30
- metaflow/plugins/metadata_providers/local.py +76 -82
- metaflow/plugins/metadata_providers/service.py +13 -9
- metaflow/plugins/metadata_providers/spin.py +16 -0
- metaflow/plugins/package_cli.py +36 -24
- metaflow/plugins/parallel_decorator.py +11 -2
- metaflow/plugins/parsers.py +16 -0
- metaflow/plugins/pypi/bootstrap.py +7 -1
- metaflow/plugins/pypi/conda_decorator.py +41 -82
- metaflow/plugins/pypi/conda_environment.py +14 -6
- metaflow/plugins/pypi/micromamba.py +9 -1
- metaflow/plugins/pypi/pip.py +41 -5
- metaflow/plugins/pypi/pypi_decorator.py +4 -4
- metaflow/plugins/pypi/utils.py +22 -0
- metaflow/plugins/secrets/__init__.py +3 -0
- metaflow/plugins/secrets/secrets_decorator.py +14 -178
- metaflow/plugins/secrets/secrets_func.py +49 -0
- metaflow/plugins/secrets/secrets_spec.py +101 -0
- metaflow/plugins/secrets/utils.py +74 -0
- metaflow/plugins/test_unbounded_foreach_decorator.py +2 -2
- metaflow/plugins/timeout_decorator.py +0 -1
- metaflow/plugins/uv/bootstrap.py +29 -1
- metaflow/plugins/uv/uv_environment.py +5 -3
- metaflow/pylint_wrapper.py +5 -1
- metaflow/runner/click_api.py +79 -26
- metaflow/runner/deployer.py +208 -6
- metaflow/runner/deployer_impl.py +32 -12
- metaflow/runner/metaflow_runner.py +266 -33
- metaflow/runner/subprocess_manager.py +21 -1
- metaflow/runner/utils.py +27 -16
- metaflow/runtime.py +660 -66
- metaflow/task.py +255 -26
- metaflow/user_configs/config_options.py +33 -21
- metaflow/user_configs/config_parameters.py +220 -58
- metaflow/user_decorators/__init__.py +0 -0
- metaflow/user_decorators/common.py +144 -0
- metaflow/user_decorators/mutable_flow.py +512 -0
- metaflow/user_decorators/mutable_step.py +424 -0
- metaflow/user_decorators/user_flow_decorator.py +264 -0
- metaflow/user_decorators/user_step_decorator.py +749 -0
- metaflow/util.py +197 -7
- metaflow/vendor.py +23 -7
- metaflow/version.py +1 -1
- {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/Makefile +13 -2
- {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/Tiltfile +107 -7
- {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/pick_services.sh +1 -0
- {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/METADATA +2 -3
- {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/RECORD +162 -121
- {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/WHEEL +1 -1
- metaflow/_vendor/v3_5/__init__.py +0 -1
- metaflow/_vendor/v3_5/importlib_metadata/__init__.py +0 -644
- metaflow/_vendor/v3_5/importlib_metadata/_compat.py +0 -152
- metaflow/_vendor/v3_5/zipp.py +0 -329
- metaflow/info_file.py +0 -25
- metaflow/package.py +0 -203
- metaflow/user_configs/config_decorators.py +0 -568
- {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/entry_points.txt +0 -0
- {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/licenses/LICENSE +0 -0
- {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.
|
|
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
|
-
|
|
193
|
+
|
|
197
194
|
if pkg_info and pkg_info.get("root_paths", None):
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
215
|
-
os.path.
|
|
216
|
-
|
|
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
|
-
|
|
220
|
-
|
|
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
|
-
|
|
319
|
-
import importlib.util
|
|
326
|
+
import importlib.util
|
|
320
327
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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(
|
|
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
|
-
#
|
|
457
|
-
#
|
|
458
|
-
#
|
|
459
|
-
#
|
|
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
|
-
|
|
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'..."
|
|
682
|
+
"Found extension package '%s' at presumptive path '%s'..."
|
|
683
|
+
% (dist_name, dist_root)
|
|
483
684
|
)
|
|
484
685
|
|
|
485
|
-
|
|
486
|
-
|
|
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
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
)
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
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
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
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
|
-
"
|
|
729
|
+
"Ignoring package at %s as it is not in the considered "
|
|
730
|
+
"directories" % addl_dir
|
|
543
731
|
)
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
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":
|
|
616
|
-
"meta_module": meta_module,
|
|
617
|
-
"
|
|
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.
|
|
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
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
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
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
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
|