metaflow 2.12.20__py2.py3-none-any.whl → 2.12.21__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 +11 -21
- metaflow/client/core.py +1 -1
- metaflow/cmd/main_cli.py +3 -2
- metaflow/extension_support/__init__.py +120 -29
- metaflow/flowspec.py +4 -0
- metaflow/info_file.py +25 -0
- metaflow/metaflow_config.py +0 -1
- metaflow/metaflow_environment.py +1 -7
- metaflow/metaflow_version.py +130 -64
- metaflow/package.py +2 -1
- metaflow/plugins/pypi/conda_decorator.py +1 -1
- metaflow/version.py +1 -1
- {metaflow-2.12.20.dist-info → metaflow-2.12.21.dist-info}/METADATA +2 -2
- {metaflow-2.12.20.dist-info → metaflow-2.12.21.dist-info}/RECORD +18 -17
- {metaflow-2.12.20.dist-info → metaflow-2.12.21.dist-info}/LICENSE +0 -0
- {metaflow-2.12.20.dist-info → metaflow-2.12.21.dist-info}/WHEEL +0 -0
- {metaflow-2.12.20.dist-info → metaflow-2.12.21.dist-info}/entry_points.txt +0 -0
- {metaflow-2.12.20.dist-info → metaflow-2.12.21.dist-info}/top_level.txt +0 -0
metaflow/__init__.py
CHANGED
@@ -42,14 +42,8 @@ If you have any questions, feel free to post a bug report/question on the
|
|
42
42
|
Metaflow GitHub page.
|
43
43
|
"""
|
44
44
|
|
45
|
-
import
|
45
|
+
import os
|
46
46
|
import sys
|
47
|
-
import types
|
48
|
-
|
49
|
-
from os import path
|
50
|
-
|
51
|
-
CURRENT_DIRECTORY = path.dirname(path.abspath(__file__))
|
52
|
-
INFO_FILE = path.join(path.dirname(CURRENT_DIRECTORY), "INFO")
|
53
47
|
|
54
48
|
from metaflow.extension_support import (
|
55
49
|
alias_submodules,
|
@@ -61,7 +55,6 @@ from metaflow.extension_support import (
|
|
61
55
|
_ext_debug,
|
62
56
|
)
|
63
57
|
|
64
|
-
|
65
58
|
# We load the module overrides *first* explicitly. Non overrides can be loaded
|
66
59
|
# in toplevel as well but these can be loaded first if needed. Note that those
|
67
60
|
# modules should be careful not to include anything in Metaflow at their top-level
|
@@ -79,9 +72,14 @@ try:
|
|
79
72
|
)
|
80
73
|
tl_module = m.module.__dict__.get("toplevel", None)
|
81
74
|
if tl_module is not None:
|
82
|
-
_tl_modules.append(
|
75
|
+
_tl_modules.append(
|
76
|
+
(
|
77
|
+
m.package_name,
|
78
|
+
".".join([EXT_PKG, m.tl_package, "toplevel", tl_module]),
|
79
|
+
)
|
80
|
+
)
|
83
81
|
_ext_debug("Got overrides to load: %s" % _override_modules)
|
84
|
-
_ext_debug("Got top-level imports: %s" % _tl_modules)
|
82
|
+
_ext_debug("Got top-level imports: %s" % str(_tl_modules))
|
85
83
|
except Exception as e:
|
86
84
|
_ext_debug("Error in importing toplevel/overrides: %s" % e)
|
87
85
|
|
@@ -153,9 +151,9 @@ if sys.version_info >= (3, 7):
|
|
153
151
|
from .runner.deployer import Deployer
|
154
152
|
from .runner.nbdeploy import NBDeployer
|
155
153
|
|
156
|
-
|
154
|
+
__ext_tl_modules__ = []
|
157
155
|
_ext_debug("Loading top-level modules")
|
158
|
-
for m in _tl_modules:
|
156
|
+
for pkg_name, m in _tl_modules:
|
159
157
|
extension_module = load_module(m)
|
160
158
|
if extension_module:
|
161
159
|
tl_package = m.split(".")[1]
|
@@ -163,15 +161,7 @@ for m in _tl_modules:
|
|
163
161
|
lazy_load_aliases(
|
164
162
|
alias_submodules(extension_module, tl_package, None, extra_indent=True)
|
165
163
|
)
|
166
|
-
|
167
|
-
if extension_module.__version__:
|
168
|
-
version_info = "%s(%s)" % (version_info, extension_module.__version__)
|
169
|
-
__version_addl__.append(version_info)
|
170
|
-
|
171
|
-
if __version_addl__:
|
172
|
-
__version_addl__ = ";".join(__version_addl__)
|
173
|
-
else:
|
174
|
-
__version_addl__ = None
|
164
|
+
__ext_tl_modules__.append((pkg_name, extension_module))
|
175
165
|
|
176
166
|
# Erase all temporary names to avoid leaking things
|
177
167
|
for _n in [
|
metaflow/client/core.py
CHANGED
@@ -34,7 +34,7 @@ from metaflow.plugins import ENVIRONMENTS, METADATA_PROVIDERS
|
|
34
34
|
from metaflow.unbounded_foreach import CONTROL_TASK_TAG
|
35
35
|
from metaflow.util import cached_property, is_stringish, resolve_identity, to_unicode
|
36
36
|
|
37
|
-
from .. import INFO_FILE
|
37
|
+
from ..info_file import INFO_FILE
|
38
38
|
from .filecache import FileCache
|
39
39
|
|
40
40
|
try:
|
metaflow/cmd/main_cli.py
CHANGED
@@ -84,12 +84,13 @@ def start(ctx):
|
|
84
84
|
|
85
85
|
import metaflow
|
86
86
|
|
87
|
+
version = get_version()
|
87
88
|
echo("Metaflow ", fg="magenta", bold=True, nl=False)
|
88
89
|
|
89
90
|
if ctx.invoked_subcommand is None:
|
90
|
-
echo("(%s): " %
|
91
|
+
echo("(%s): " % version, fg="magenta", bold=False, nl=False)
|
91
92
|
else:
|
92
|
-
echo("(%s)\n" %
|
93
|
+
echo("(%s)\n" % version, fg="magenta", bold=False)
|
93
94
|
|
94
95
|
if ctx.invoked_subcommand is None:
|
95
96
|
echo("More data science, less engineering\n", fg="magenta")
|
@@ -1,7 +1,6 @@
|
|
1
1
|
from __future__ import print_function
|
2
2
|
|
3
3
|
import importlib
|
4
|
-
import json
|
5
4
|
import os
|
6
5
|
import re
|
7
6
|
import sys
|
@@ -11,6 +10,10 @@ from collections import defaultdict, namedtuple
|
|
11
10
|
|
12
11
|
from importlib.abc import MetaPathFinder, Loader
|
13
12
|
from itertools import chain
|
13
|
+
from pathlib import Path
|
14
|
+
|
15
|
+
from metaflow.info_file import read_info_file
|
16
|
+
|
14
17
|
|
15
18
|
#
|
16
19
|
# This file provides the support for Metaflow's extension mechanism which allows
|
@@ -59,6 +62,9 @@ __all__ = (
|
|
59
62
|
"load_module",
|
60
63
|
"get_modules",
|
61
64
|
"dump_module_info",
|
65
|
+
"get_extensions_in_dir",
|
66
|
+
"extension_info",
|
67
|
+
"update_package_info",
|
62
68
|
"get_aliased_modules",
|
63
69
|
"package_mfext_package",
|
64
70
|
"package_mfext_all",
|
@@ -80,9 +86,14 @@ EXT_EXCLUDE_SUFFIXES = [".pyc"]
|
|
80
86
|
# To get verbose messages, set METAFLOW_DEBUG_EXT to 1
|
81
87
|
DEBUG_EXT = os.environ.get("METAFLOW_DEBUG_EXT", False)
|
82
88
|
|
89
|
+
# This is extracted only from environment variable and here separately from
|
90
|
+
# metaflow_config to prevent nasty circular dependencies
|
91
|
+
EXTENSIONS_SEARCH_DIRS = os.environ.get("METAFLOW_EXTENSIONS_SEARCH_DIRS", "").split(
|
92
|
+
os.pathsep
|
93
|
+
)
|
83
94
|
|
84
95
|
MFExtPackage = namedtuple("MFExtPackage", "package_name tl_package config_module")
|
85
|
-
MFExtModule = namedtuple("MFExtModule", "tl_package module")
|
96
|
+
MFExtModule = namedtuple("MFExtModule", "package_name tl_package module")
|
86
97
|
|
87
98
|
|
88
99
|
def load_module(module_name):
|
@@ -113,17 +124,64 @@ def get_modules(extension_point):
|
|
113
124
|
return modules_to_load
|
114
125
|
|
115
126
|
|
116
|
-
def dump_module_info():
|
117
|
-
|
127
|
+
def dump_module_info(all_packages=None, pkgs_per_extension_point=None):
|
128
|
+
if all_packages is None:
|
129
|
+
all_packages = _all_packages
|
130
|
+
if pkgs_per_extension_point is None:
|
131
|
+
pkgs_per_extension_point = _pkgs_per_extension_point
|
132
|
+
|
133
|
+
_filter_files_all(all_packages)
|
118
134
|
sanitized_all_packages = dict()
|
119
135
|
# Strip out root_paths (we don't need it and no need to expose user's dir structure)
|
120
|
-
for k, v in
|
136
|
+
for k, v in all_packages.items():
|
121
137
|
sanitized_all_packages[k] = {
|
122
138
|
"root_paths": None,
|
123
139
|
"meta_module": v["meta_module"],
|
124
140
|
"files": v["files"],
|
141
|
+
"version": v["version"],
|
142
|
+
"package_version": v.get("package_version", "<unk>"),
|
143
|
+
"extension_name": v.get("extension_name", "<unk>"),
|
125
144
|
}
|
126
|
-
return "ext_info", [sanitized_all_packages,
|
145
|
+
return "ext_info", [sanitized_all_packages, pkgs_per_extension_point]
|
146
|
+
|
147
|
+
|
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
|
+
return _get_extension_packages(ignore_info_file=True, restrict_to_directories=[d])
|
153
|
+
|
154
|
+
|
155
|
+
def extension_info(packages=None):
|
156
|
+
if packages is None:
|
157
|
+
packages = _all_packages
|
158
|
+
# Returns information about installed extensions so it it can be stored in
|
159
|
+
# _graph_info.
|
160
|
+
return {
|
161
|
+
"installed": {
|
162
|
+
k: {
|
163
|
+
"dist_version": v["version"],
|
164
|
+
"package_version": v.get("package_version", "<unk>"),
|
165
|
+
"extension_name": v.get("extension_name", "<unk>"),
|
166
|
+
}
|
167
|
+
for k, v in packages.items()
|
168
|
+
},
|
169
|
+
}
|
170
|
+
|
171
|
+
|
172
|
+
def update_package_info(pkg_to_update=None, package_name=None, **kwargs):
|
173
|
+
pkg = None
|
174
|
+
if pkg_to_update:
|
175
|
+
pkg = pkg_to_update
|
176
|
+
elif package_name:
|
177
|
+
pkg = _all_packages.get(package_name)
|
178
|
+
for k, v in kwargs.items():
|
179
|
+
if k in pkg:
|
180
|
+
raise ValueError(
|
181
|
+
"Trying to overwrite existing key '%s' for package %s" % (k, str(pkg))
|
182
|
+
)
|
183
|
+
pkg[k] = v
|
184
|
+
return pkg
|
127
185
|
|
128
186
|
|
129
187
|
def get_aliased_modules():
|
@@ -134,8 +192,8 @@ def package_mfext_package(package_name):
|
|
134
192
|
from metaflow.util import to_unicode
|
135
193
|
|
136
194
|
_ext_debug("Packaging '%s'" % package_name)
|
137
|
-
_filter_files_package(package_name)
|
138
195
|
pkg_info = _all_packages.get(package_name, None)
|
196
|
+
_filter_files_package(pkg_info)
|
139
197
|
if pkg_info and pkg_info.get("root_paths", None):
|
140
198
|
single_path = len(pkg_info["root_paths"]) == 1
|
141
199
|
for p in pkg_info["root_paths"]:
|
@@ -296,7 +354,7 @@ def _ext_debug(*args, **kwargs):
|
|
296
354
|
print(init_str, *args, **kwargs)
|
297
355
|
|
298
356
|
|
299
|
-
def _get_extension_packages():
|
357
|
+
def _get_extension_packages(ignore_info_file=False, restrict_to_directories=None):
|
300
358
|
if not _mfext_supported:
|
301
359
|
_ext_debug("Not supported for your Python version -- 3.4+ is needed")
|
302
360
|
return {}, {}
|
@@ -304,20 +362,20 @@ def _get_extension_packages():
|
|
304
362
|
# If we have an INFO file with the appropriate information (if running from a saved
|
305
363
|
# code package for example), we use that directly
|
306
364
|
# Pre-compute on _extension_points
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
365
|
+
info_content = read_info_file()
|
366
|
+
if not ignore_info_file and info_content:
|
367
|
+
all_pkg, ext_to_pkg = info_content.get("ext_info", (None, None))
|
368
|
+
if all_pkg is not None and ext_to_pkg is not None:
|
369
|
+
_ext_debug("Loading pre-computed information from INFO file")
|
370
|
+
# We need to properly convert stuff in ext_to_pkg
|
371
|
+
for k, v in ext_to_pkg.items():
|
372
|
+
v = [MFExtPackage(*d) for d in v]
|
373
|
+
ext_to_pkg[k] = v
|
374
|
+
return all_pkg, ext_to_pkg
|
375
|
+
|
376
|
+
# Late import to prevent some circular nastiness
|
377
|
+
if restrict_to_directories is None and EXTENSIONS_SEARCH_DIRS != [""]:
|
378
|
+
restrict_to_directories = EXTENSIONS_SEARCH_DIRS
|
321
379
|
|
322
380
|
# Check if we even have extensions
|
323
381
|
try:
|
@@ -331,6 +389,11 @@ def _get_extension_packages():
|
|
331
389
|
raise
|
332
390
|
return {}, {}
|
333
391
|
|
392
|
+
if restrict_to_directories:
|
393
|
+
restrict_to_directories = [
|
394
|
+
Path(p).resolve().as_posix() for p in restrict_to_directories
|
395
|
+
]
|
396
|
+
|
334
397
|
# There are two "types" of packages:
|
335
398
|
# - those installed on the system (distributions)
|
336
399
|
# - those present in the PYTHONPATH
|
@@ -343,8 +406,12 @@ def _get_extension_packages():
|
|
343
406
|
# At this point, we look at all the paths and create a set. As we find distributions
|
344
407
|
# that match it, we will remove from the set and then will be left with any
|
345
408
|
# PYTHONPATH "packages"
|
346
|
-
all_paths = set(extensions_module.__path__)
|
409
|
+
all_paths = set(Path(p).resolve().as_posix() for p in extensions_module.__path__)
|
347
410
|
_ext_debug("Found packages present at %s" % str(all_paths))
|
411
|
+
if restrict_to_directories:
|
412
|
+
_ext_debug(
|
413
|
+
"Processed packages will be restricted to %s" % str(restrict_to_directories)
|
414
|
+
)
|
348
415
|
|
349
416
|
list_ext_points = [x.split(".") for x in _extension_points]
|
350
417
|
init_ext_points = [x[0] for x in list_ext_points]
|
@@ -391,9 +458,20 @@ def _get_extension_packages():
|
|
391
458
|
# This is not 100% accurate because it is possible that at the same
|
392
459
|
# location there is a package and a non-package, but this is extremely
|
393
460
|
# unlikely so we are going to ignore this case.
|
394
|
-
dist_root = dist.locate_file(EXT_PKG).as_posix()
|
461
|
+
dist_root = dist.locate_file(EXT_PKG).resolve().as_posix()
|
395
462
|
all_paths.discard(dist_root)
|
396
463
|
dist_name = dist.metadata["Name"]
|
464
|
+
dist_version = dist.metadata["Version"]
|
465
|
+
if restrict_to_directories:
|
466
|
+
parent_dirs = list(
|
467
|
+
p.as_posix() for p in Path(dist_root).resolve().parents
|
468
|
+
)
|
469
|
+
if all(p not in parent_dirs for p in restrict_to_directories):
|
470
|
+
_ext_debug(
|
471
|
+
"Ignoring package at %s as it is not in the considered directories"
|
472
|
+
% dist_root
|
473
|
+
)
|
474
|
+
continue
|
397
475
|
if dist_name in mf_ext_packages:
|
398
476
|
_ext_debug(
|
399
477
|
"Ignoring duplicate package '%s' (duplicate paths in sys.path? (%s))"
|
@@ -537,6 +615,7 @@ def _get_extension_packages():
|
|
537
615
|
"root_paths": [dist_root],
|
538
616
|
"meta_module": meta_module,
|
539
617
|
"files": files_to_include,
|
618
|
+
"version": dist_version,
|
540
619
|
}
|
541
620
|
# At this point, we have all the packages that contribute to EXT_PKG,
|
542
621
|
# we now check to see if there is an order to respect based on dependencies. We will
|
@@ -605,6 +684,16 @@ def _get_extension_packages():
|
|
605
684
|
if len(all_paths_list) > 0:
|
606
685
|
_ext_debug("Non installed packages present at %s" % str(all_paths))
|
607
686
|
for package_count, package_path in enumerate(all_paths_list):
|
687
|
+
if restrict_to_directories:
|
688
|
+
parent_dirs = list(
|
689
|
+
p.as_posix() for p in Path(package_path).resolve().parents
|
690
|
+
)
|
691
|
+
if all(p not in parent_dirs for p in restrict_to_directories):
|
692
|
+
_ext_debug(
|
693
|
+
"Ignoring non-installed package at %s as it is not in "
|
694
|
+
"the considered directories" % package_path
|
695
|
+
)
|
696
|
+
continue
|
608
697
|
# We give an alternate name for the visible package name. It is
|
609
698
|
# not exposed to the end user but used to refer to the package, and it
|
610
699
|
# doesn't provide much additional information to have the full path
|
@@ -740,6 +829,7 @@ def _get_extension_packages():
|
|
740
829
|
"root_paths": [package_path],
|
741
830
|
"meta_module": meta_module,
|
742
831
|
"files": files_to_include,
|
832
|
+
"version": "_local_",
|
743
833
|
}
|
744
834
|
|
745
835
|
# Sanity check that we only have one package per configuration file.
|
@@ -868,12 +958,13 @@ def _get_extension_config(distribution_name, tl_pkg, extension_point, config_mod
|
|
868
958
|
_ext_debug("Package '%s' is rooted at %s" % (distribution_name, root_paths))
|
869
959
|
_all_packages[distribution_name]["root_paths"] = root_paths
|
870
960
|
|
871
|
-
return MFExtModule(
|
961
|
+
return MFExtModule(
|
962
|
+
package_name=distribution_name, tl_package=tl_pkg, module=extension_module
|
963
|
+
)
|
872
964
|
return None
|
873
965
|
|
874
966
|
|
875
|
-
def _filter_files_package(
|
876
|
-
pkg = _all_packages.get(package_name)
|
967
|
+
def _filter_files_package(pkg):
|
877
968
|
if pkg and pkg["root_paths"] and pkg["meta_module"]:
|
878
969
|
meta_module = _attempt_load_module(pkg["meta_module"])
|
879
970
|
if meta_module:
|
@@ -902,8 +993,8 @@ def _filter_files_package(package_name):
|
|
902
993
|
pkg["files"] = new_files
|
903
994
|
|
904
995
|
|
905
|
-
def _filter_files_all():
|
906
|
-
for p in
|
996
|
+
def _filter_files_all(all_packages):
|
997
|
+
for p in all_packages.values():
|
907
998
|
_filter_files_package(p)
|
908
999
|
|
909
1000
|
|
metaflow/flowspec.py
CHANGED
@@ -15,6 +15,9 @@ from .exception import (
|
|
15
15
|
MissingInMergeArtifactsException,
|
16
16
|
UnhandledInMergeArtifactsException,
|
17
17
|
)
|
18
|
+
|
19
|
+
from .extension_support import extension_info
|
20
|
+
|
18
21
|
from .graph import FlowGraph
|
19
22
|
from .unbounded_foreach import UnboundedForeachInput
|
20
23
|
from .util import to_pod
|
@@ -208,6 +211,7 @@ class FlowSpec(metaclass=_FlowSpecMeta):
|
|
208
211
|
for deco in flow_decorators(self)
|
209
212
|
if not deco.name.startswith("_")
|
210
213
|
],
|
214
|
+
"extensions": extension_info(),
|
211
215
|
}
|
212
216
|
self._graph_info = graph_info
|
213
217
|
|
metaflow/info_file.py
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
import json
|
2
|
+
|
3
|
+
from os import path
|
4
|
+
|
5
|
+
CURRENT_DIRECTORY = path.dirname(path.abspath(__file__))
|
6
|
+
INFO_FILE = path.join(path.dirname(CURRENT_DIRECTORY), "INFO")
|
7
|
+
|
8
|
+
_info_file_content = None
|
9
|
+
_info_file_present = None
|
10
|
+
|
11
|
+
|
12
|
+
def read_info_file():
|
13
|
+
global _info_file_content
|
14
|
+
global _info_file_present
|
15
|
+
if _info_file_present is None:
|
16
|
+
_info_file_present = path.exists(INFO_FILE)
|
17
|
+
if _info_file_present:
|
18
|
+
try:
|
19
|
+
with open(INFO_FILE, "r", encoding="utf-8") as contents:
|
20
|
+
_info_file_content = json.load(contents)
|
21
|
+
except IOError:
|
22
|
+
pass
|
23
|
+
if _info_file_present:
|
24
|
+
return _info_file_content
|
25
|
+
return None
|
metaflow/metaflow_config.py
CHANGED
metaflow/metaflow_environment.py
CHANGED
@@ -9,8 +9,6 @@ from metaflow.extension_support import dump_module_info
|
|
9
9
|
from metaflow.mflog import BASH_MFLOG
|
10
10
|
from . import R
|
11
11
|
|
12
|
-
version_cache = None
|
13
|
-
|
14
12
|
|
15
13
|
class InvalidEnvironmentException(MetaflowException):
|
16
14
|
headline = "Incompatible environment"
|
@@ -180,10 +178,6 @@ class MetaflowEnvironment(object):
|
|
180
178
|
return cmds
|
181
179
|
|
182
180
|
def get_environment_info(self, include_ext_info=False):
|
183
|
-
global version_cache
|
184
|
-
if version_cache is None:
|
185
|
-
version_cache = metaflow_version.get_version()
|
186
|
-
|
187
181
|
# note that this dict goes into the code package
|
188
182
|
# so variables here should be relatively stable (no
|
189
183
|
# timestamps) so the hash won't change all the time
|
@@ -197,7 +191,7 @@ class MetaflowEnvironment(object):
|
|
197
191
|
"use_r": R.use_r(),
|
198
192
|
"python_version": sys.version,
|
199
193
|
"python_version_code": "%d.%d.%d" % sys.version_info[:3],
|
200
|
-
"metaflow_version":
|
194
|
+
"metaflow_version": metaflow_version.get_version(),
|
201
195
|
"script": os.path.basename(os.path.abspath(sys.argv[0])),
|
202
196
|
}
|
203
197
|
if R.use_r():
|
metaflow/metaflow_version.py
CHANGED
@@ -7,11 +7,15 @@ See the documentation of get_version for more information
|
|
7
7
|
|
8
8
|
# This file is adapted from https://github.com/aebrahim/python-git-version
|
9
9
|
|
10
|
-
|
11
|
-
from os import path, name,
|
12
|
-
import json
|
10
|
+
import subprocess
|
11
|
+
from os import path, name, environ, listdir
|
13
12
|
|
14
|
-
from metaflow import
|
13
|
+
from metaflow.extension_support import update_package_info
|
14
|
+
from metaflow.info_file import CURRENT_DIRECTORY, read_info_file
|
15
|
+
|
16
|
+
|
17
|
+
# True/False correspond to the value `public`` in get_version
|
18
|
+
_version_cache = {True: None, False: None}
|
15
19
|
|
16
20
|
__all__ = ("get_version",)
|
17
21
|
|
@@ -57,87 +61,149 @@ if name == "nt":
|
|
57
61
|
GIT_COMMAND = find_git_on_windows()
|
58
62
|
|
59
63
|
|
60
|
-
def call_git_describe(abbrev=7):
|
64
|
+
def call_git_describe(file_to_check, abbrev=7):
|
61
65
|
"""return the string output of git describe"""
|
62
66
|
try:
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
67
|
+
wd = path.dirname(file_to_check)
|
68
|
+
filename = path.basename(file_to_check)
|
69
|
+
|
70
|
+
# First check if the file is tracked in the GIT repository we are in
|
71
|
+
# We do this because in some setups and for some bizarre reason, python files
|
72
|
+
# are installed directly into a git repository (I am looking at you brew). We
|
73
|
+
# don't want to consider this a GIT install in that case.
|
74
|
+
args = [GIT_COMMAND, "ls-files", "--error-unmatch", filename]
|
75
|
+
git_return_code = subprocess.run(
|
76
|
+
args,
|
77
|
+
cwd=wd,
|
78
|
+
stderr=subprocess.DEVNULL,
|
79
|
+
stdout=subprocess.DEVNULL,
|
80
|
+
check=False,
|
81
|
+
).returncode
|
82
|
+
|
83
|
+
if git_return_code != 0:
|
84
|
+
return None
|
85
|
+
|
86
|
+
args = [
|
87
|
+
GIT_COMMAND,
|
88
|
+
"describe",
|
89
|
+
"--tags",
|
90
|
+
"--dirty",
|
91
|
+
"--long",
|
92
|
+
"--abbrev=%d" % abbrev,
|
93
|
+
]
|
94
|
+
return (
|
95
|
+
subprocess.check_output(args, cwd=wd, stderr=subprocess.DEVNULL)
|
96
|
+
.decode("ascii")
|
97
|
+
.strip()
|
98
|
+
)
|
99
|
+
|
100
|
+
except (OSError, subprocess.CalledProcessError):
|
85
101
|
return None
|
86
102
|
|
87
103
|
|
88
|
-
def format_git_describe(git_str,
|
104
|
+
def format_git_describe(git_str, public=False):
|
89
105
|
"""format the result of calling 'git describe' as a python version"""
|
90
106
|
if git_str is None:
|
91
107
|
return None
|
92
|
-
|
93
|
-
|
108
|
+
splits = git_str.split("-")
|
109
|
+
if len(splits) == 4:
|
110
|
+
# Formatted as <tag>-<post>-<hash>-dirty
|
111
|
+
tag, post, h = splits[:3]
|
112
|
+
dirty = "-" + splits[3]
|
94
113
|
else:
|
95
|
-
#
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
114
|
+
# Formatted as <tag>-<post>-<hash>
|
115
|
+
tag, post, h = splits
|
116
|
+
dirty = ""
|
117
|
+
if post == "0":
|
118
|
+
if public:
|
119
|
+
return tag
|
120
|
+
return tag + dirty
|
121
|
+
|
122
|
+
if public:
|
123
|
+
return "%s.post%s" % (tag, post)
|
124
|
+
|
125
|
+
return "%s.post%s-git%s%s" % (tag, post, h[1:], dirty)
|
102
126
|
|
103
127
|
|
104
128
|
def read_info_version():
|
105
129
|
"""Read version information from INFO file"""
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
return None
|
130
|
+
info_file = read_info_file()
|
131
|
+
if info_file:
|
132
|
+
return info_file.get("metaflow_version")
|
133
|
+
return None
|
111
134
|
|
112
135
|
|
113
|
-
def get_version(
|
136
|
+
def get_version(public=False):
|
114
137
|
"""Tracks the version number.
|
115
138
|
|
116
|
-
|
117
|
-
When True, this function returns a version
|
118
|
-
|
119
|
-
|
139
|
+
public: bool
|
140
|
+
When True, this function returns a *public* version specification which
|
141
|
+
doesn't include any local information (dirtiness or hash). See
|
142
|
+
https://packaging.python.org/en/latest/specifications/version-specifiers/#version-scheme
|
120
143
|
|
121
|
-
|
122
|
-
|
144
|
+
We first check the INFO file to see if we recorded a version of Metaflow. If there
|
145
|
+
is none, we check if we are in a GIT repository and if so, form the version
|
146
|
+
from that.
|
123
147
|
|
124
|
-
Otherwise, the version
|
125
|
-
|
126
|
-
If even that information isn't available (likely when executing on a
|
127
|
-
remote cloud instance), the version information is returned from INFO file
|
128
|
-
in the current directory.
|
148
|
+
Otherwise, we return the version of Metaflow that was installed.
|
129
149
|
|
130
150
|
"""
|
131
151
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
152
|
+
global _version_cache
|
153
|
+
|
154
|
+
# To get the version we do the following:
|
155
|
+
# - Check if we have a cached version. If so, return that
|
156
|
+
# - Then check if we have an INFO file present. If so, use that as it is
|
157
|
+
# the most reliable way to get the version. In particular, when running remotely,
|
158
|
+
# metaflow is installed in a directory and if any extension is using distutils to
|
159
|
+
# determine its version, this would return None and querying the version directly
|
160
|
+
# from the extension would fail to produce the correct result
|
161
|
+
# - Then if we are in the GIT repository and if so, use the git describe
|
162
|
+
# - If we don't have an INFO file, we look at the version information that is
|
163
|
+
# populated by metaflow and the extensions.
|
164
|
+
|
165
|
+
if _version_cache[public] is not None:
|
166
|
+
return _version_cache[public]
|
167
|
+
|
168
|
+
version = (
|
169
|
+
read_info_version()
|
170
|
+
) # Version info is cached in INFO file; includes extension info
|
171
|
+
|
172
|
+
if version:
|
173
|
+
_version_cache[public] = version
|
174
|
+
return version
|
175
|
+
|
176
|
+
# Get the version for Metaflow, favor the GIT version
|
177
|
+
import metaflow
|
178
|
+
|
179
|
+
version = format_git_describe(
|
180
|
+
call_git_describe(file_to_check=metaflow.__file__), public=public
|
181
|
+
)
|
182
|
+
if version is None:
|
137
183
|
version = metaflow.__version__
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
184
|
+
|
185
|
+
# Look for extensions and compute their versions. Properly formed extensions have
|
186
|
+
# a toplevel file which will contain a __mf_extensions__ value and a __version__
|
187
|
+
# value. We already saved the properly formed modules when loading metaflow in
|
188
|
+
# __ext_tl_modules__.
|
189
|
+
ext_versions = []
|
190
|
+
for pkg_name, extension_module in metaflow.__ext_tl_modules__:
|
191
|
+
ext_name = getattr(extension_module, "__mf_extensions__", "<unk>")
|
192
|
+
ext_version = format_git_describe(
|
193
|
+
call_git_describe(file_to_check=extension_module.__file__), public=public
|
194
|
+
)
|
195
|
+
if ext_version is None:
|
196
|
+
ext_version = getattr(extension_module, "__version__", "<unk>")
|
197
|
+
# Update the package information about reported version for the extension
|
198
|
+
update_package_info(
|
199
|
+
package_name=pkg_name,
|
200
|
+
extension_name=ext_name,
|
201
|
+
package_version=ext_version,
|
202
|
+
)
|
203
|
+
ext_versions.append("%s(%s)" % (ext_name, ext_version))
|
204
|
+
|
205
|
+
# We now have all the information about extensions so we can form the final string
|
206
|
+
if ext_versions:
|
207
|
+
version = version + "+" + ";".join(ext_versions)
|
208
|
+
_version_cache[public] = version
|
143
209
|
return version
|
metaflow/package.py
CHANGED
@@ -10,7 +10,8 @@ from .extension_support import EXT_PKG, package_mfext_all
|
|
10
10
|
from .metaflow_config import DEFAULT_PACKAGE_SUFFIXES
|
11
11
|
from .exception import MetaflowException
|
12
12
|
from .util import to_unicode
|
13
|
-
from . import R
|
13
|
+
from . import R
|
14
|
+
from .info_file import INFO_FILE
|
14
15
|
|
15
16
|
DEFAULT_SUFFIXES_LIST = DEFAULT_PACKAGE_SUFFIXES.split(",")
|
16
17
|
METAFLOW_SUFFIXES_LIST = [".py", ".html", ".css", ".js"]
|
@@ -12,7 +12,7 @@ from metaflow.metadata import MetaDatum
|
|
12
12
|
from metaflow.metaflow_environment import InvalidEnvironmentException
|
13
13
|
from metaflow.util import get_metaflow_root
|
14
14
|
|
15
|
-
from ... import INFO_FILE
|
15
|
+
from ...info_file import INFO_FILE
|
16
16
|
|
17
17
|
|
18
18
|
class CondaStepDecorator(StepDecorator):
|
metaflow/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
metaflow_version = "2.12.
|
1
|
+
metaflow_version = "2.12.21"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: metaflow
|
3
|
-
Version: 2.12.
|
3
|
+
Version: 2.12.21
|
4
4
|
Summary: Metaflow: More Data Science, Less Engineering
|
5
5
|
Author: Metaflow Developers
|
6
6
|
Author-email: help@metaflow.org
|
@@ -26,7 +26,7 @@ License-File: LICENSE
|
|
26
26
|
Requires-Dist: requests
|
27
27
|
Requires-Dist: boto3
|
28
28
|
Provides-Extra: stubs
|
29
|
-
Requires-Dist: metaflow-stubs==2.12.
|
29
|
+
Requires-Dist: metaflow-stubs==2.12.21; extra == "stubs"
|
30
30
|
|
31
31
|

|
32
32
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
metaflow/R.py,sha256=CqVfIatvmjciuICNnoyyNGrwE7Va9iXfLdFbQa52hwA,3958
|
2
|
-
metaflow/__init__.py,sha256=
|
2
|
+
metaflow/__init__.py,sha256=9pnkRH00bbtzaQDSxEspANaB3r6YDC37_EjX7GxtJug,5641
|
3
3
|
metaflow/cards.py,sha256=tP1_RrtmqdFh741pqE4t98S7SA0MtGRlGvRICRZF1Mg,426
|
4
4
|
metaflow/cli.py,sha256=fa6dx6F2PKtq0oeMxRM2efA9LyE108jGZQvYCtiUS94,34274
|
5
5
|
metaflow/cli_args.py,sha256=lcgBGNTvfaiPxiUnejAe60Upt9swG6lRy1_3OqbU6MY,2616
|
@@ -10,20 +10,21 @@ metaflow/decorators.py,sha256=hbJwRlZuLbl6t7fOsTiH7Sux4Lx6zgEEpldIXoM5TQc,21540
|
|
10
10
|
metaflow/event_logger.py,sha256=joTVRqZPL87nvah4ZOwtqWX8NeraM_CXKXXGVpKGD8o,780
|
11
11
|
metaflow/events.py,sha256=ahjzkSbSnRCK9RZ-9vTfUviz_6gMvSO9DGkJ86X80-k,5300
|
12
12
|
metaflow/exception.py,sha256=KC1LHJQzzYkWib0DeQ4l_A2r8VaudywsSqIQuq1RDZU,4954
|
13
|
-
metaflow/flowspec.py,sha256=
|
13
|
+
metaflow/flowspec.py,sha256=4w7Hm1aFSmVBOq0x9-LtFAq4eDjFb5F6Rdyh_NW41BQ,27270
|
14
14
|
metaflow/graph.py,sha256=HFJ7V_bPSht_NHIm8BejrSqOX2fyBQpVOczRCliRw08,11975
|
15
15
|
metaflow/includefile.py,sha256=yHczcZ_U0SrasxSNhZb3DIBzx8UZnrJCl3FzvpEQLOA,19753
|
16
|
+
metaflow/info_file.py,sha256=wtf2_F0M6dgiUu74AFImM8lfy5RrUw5Yj7Rgs2swKRY,686
|
16
17
|
metaflow/integrations.py,sha256=LlsaoePRg03DjENnmLxZDYto3NwWc9z_PtU6nJxLldg,1480
|
17
18
|
metaflow/lint.py,sha256=5rj1MlpluxyPTSINjtMoJ7viotyNzfjtBJSAihlAwMU,10870
|
18
|
-
metaflow/metaflow_config.py,sha256=
|
19
|
+
metaflow/metaflow_config.py,sha256=cFd_RaZnxAWQX1kNEA-Y6HlxLvTpL-OM-d1M0CQoz_M,22852
|
19
20
|
metaflow/metaflow_config_funcs.py,sha256=5GlvoafV6SxykwfL8D12WXSfwjBN_NsyuKE_Q3gjGVE,6738
|
20
21
|
metaflow/metaflow_current.py,sha256=pC-EMnAsnvBLvLd61W6MvfiCKcboryeui9f6r8z_sg8,7161
|
21
|
-
metaflow/metaflow_environment.py,sha256=
|
22
|
+
metaflow/metaflow_environment.py,sha256=rojFyGdyY56sN1HaEb1-0XX53Q3XPNnl0SaH-8xXZ8w,7987
|
22
23
|
metaflow/metaflow_profile.py,sha256=jKPEW-hmAQO-htSxb9hXaeloLacAh41A35rMZH6G8pA,418
|
23
|
-
metaflow/metaflow_version.py,sha256=
|
24
|
+
metaflow/metaflow_version.py,sha256=lOsrhMfHdKXBPS-673sZb48dK2FJhr34rIwLqMFeG9I,7291
|
24
25
|
metaflow/monitor.py,sha256=T0NMaBPvXynlJAO_avKtk8OIIRMyEuMAyF8bIp79aZU,5323
|
25
26
|
metaflow/multicore_utils.py,sha256=vdTNgczVLODifscUbbveJbuSDOl3Y9pAxhr7sqYiNf4,4760
|
26
|
-
metaflow/package.py,sha256=
|
27
|
+
metaflow/package.py,sha256=QutDP6WzjwGk1UCKXqBfXa9F10Q--FlRr0J7fwlple0,7399
|
27
28
|
metaflow/parameters.py,sha256=l8qnhBG9C4wf_FkXWjq5sapUA6npLdR7pyB0PPQ-KF0,15712
|
28
29
|
metaflow/procpoll.py,sha256=U2tE4iK_Mwj2WDyVTx_Uglh6xZ-jixQOo4wrM9OOhxg,2859
|
29
30
|
metaflow/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -35,7 +36,7 @@ metaflow/tuple_util.py,sha256=_G5YIEhuugwJ_f6rrZoelMFak3DqAR2tt_5CapS1XTY,830
|
|
35
36
|
metaflow/unbounded_foreach.py,sha256=p184WMbrMJ3xKYHwewj27ZhRUsSj_kw1jlye5gA9xJk,387
|
36
37
|
metaflow/util.py,sha256=olAvJK3y1it_k99MhLulTaAJo7OFVt5rnrD-ulIFLCU,13616
|
37
38
|
metaflow/vendor.py,sha256=FchtA9tH22JM-eEtJ2c9FpUdMn8sSb1VHuQS56EcdZk,5139
|
38
|
-
metaflow/version.py,sha256
|
39
|
+
metaflow/version.py,sha256=-0We4urba1UC7-H1mYhm1sD_smCQ3Vn5btzNTU06WJU,29
|
39
40
|
metaflow/_vendor/__init__.py,sha256=y_CiwUD3l4eAKvTVDZeqgVujMy31cAM1qjAB-HfI-9s,353
|
40
41
|
metaflow/_vendor/typing_extensions.py,sha256=0nUs5p1A_UrZigrAVBoOEM6TxU37zzPDUtiij1ZwpNc,110417
|
41
42
|
metaflow/_vendor/zipp.py,sha256=ajztOH-9I7KA_4wqDYygtHa6xUBVZgFpmZ8FE74HHHI,8425
|
@@ -110,11 +111,11 @@ metaflow/_vendor/v3_6/importlib_metadata/_meta.py,sha256=_F48Hu_jFxkfKWz5wcYS8vO
|
|
110
111
|
metaflow/_vendor/v3_6/importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166
|
111
112
|
metaflow/_vendor/v3_6/importlib_metadata/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
112
113
|
metaflow/client/__init__.py,sha256=1GtQB4Y_CBkzaxg32L1syNQSlfj762wmLrfrDxGi1b8,226
|
113
|
-
metaflow/client/core.py,sha256=
|
114
|
+
metaflow/client/core.py,sha256=jnFTP_XLTLJCHRqaxGKYDn1ZwB_v6a1AAkULlLmJGOk,74171
|
114
115
|
metaflow/client/filecache.py,sha256=Wy0yhhCqC1JZgebqi7z52GCwXYnkAqMZHTtxThvwBgM,15229
|
115
116
|
metaflow/cmd/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
116
117
|
metaflow/cmd/configure_cmd.py,sha256=o-DKnUf2FBo_HiMVyoyzQaGBSMtpbEPEdFTQZ0hkU-k,33396
|
117
|
-
metaflow/cmd/main_cli.py,sha256=
|
118
|
+
metaflow/cmd/main_cli.py,sha256=E546zT_jYQKysmjwfpEgzZd5QMsyirs28M2s0OPU93E,2966
|
118
119
|
metaflow/cmd/tutorials_cmd.py,sha256=8FdlKkicTOhCIDKcBR5b0Oz6giDvS-EMY3o9skIrRqw,5156
|
119
120
|
metaflow/cmd/util.py,sha256=jS_0rUjOnGGzPT65fzRLdGjrYAOOLA4jU2S0HJLV0oc,406
|
120
121
|
metaflow/cmd/develop/__init__.py,sha256=p1Sy8yU1MEKSrH5ttOWOZvNcI1qYu6J6jghdTHwPgOw,689
|
@@ -128,7 +129,7 @@ metaflow/datastore/exceptions.py,sha256=r7Ab5FvHIzyFh6kwiptA1lO5nLqWg0xRBoeYGefv
|
|
128
129
|
metaflow/datastore/flow_datastore.py,sha256=kbJcOLYnvPHgJfZ_WWkD9LJSX1PHI1K6f9oVUu08A9U,10235
|
129
130
|
metaflow/datastore/inputs.py,sha256=i43dXr2xvgtsgKMO9allgCR18bk80GeayeQFyUTH36w,449
|
130
131
|
metaflow/datastore/task_datastore.py,sha256=-aDvRUUEaWq0chiGzCCSYmsTP8T-gjs0eowvy2Xyaug,36167
|
131
|
-
metaflow/extension_support/__init__.py,sha256=
|
132
|
+
metaflow/extension_support/__init__.py,sha256=2z0c4R8zsVmEFOMGT2Jujsl6xveDVa9KLll7moL58NE,52984
|
132
133
|
metaflow/extension_support/_empty_file.py,sha256=HENjnM4uAfeNygxMB_feCCWORFoSat9n_QwzSx2oXPw,109
|
133
134
|
metaflow/extension_support/cmd.py,sha256=hk8iBUUINqvKCDxInKgWpum8ThiRZtHSJP7qBASHzl8,5711
|
134
135
|
metaflow/extension_support/integrations.py,sha256=AWAh-AZ-vo9IxuAVEjGw3s8p_NMm2DKHYx10oC51gPU,5506
|
@@ -291,7 +292,7 @@ metaflow/plugins/metadata/local.py,sha256=YhLJC5zjVJrvQFIyQ92ZBByiUmhCC762RUX7IT
|
|
291
292
|
metaflow/plugins/metadata/service.py,sha256=ihq5F7KQZlxvYwzH_-jyP2aWN_I96i2vp92j_d697s8,20204
|
292
293
|
metaflow/plugins/pypi/__init__.py,sha256=0YFZpXvX7HCkyBFglatual7XGifdA1RwC3U4kcizyak,1037
|
293
294
|
metaflow/plugins/pypi/bootstrap.py,sha256=LSRjpDhtpvYqCC0lDPIWvZsrHW7PXx626NDzdYjFeVM,5224
|
294
|
-
metaflow/plugins/pypi/conda_decorator.py,sha256=
|
295
|
+
metaflow/plugins/pypi/conda_decorator.py,sha256=fPeXxvmg51oSFTnlguNlcWUIdXHA9OuMnp9ElaxQPFo,15695
|
295
296
|
metaflow/plugins/pypi/conda_environment.py,sha256=--q-8lypKupCdGsASpqABNpNqRxtQi6UCDgq8iHDFe4,19476
|
296
297
|
metaflow/plugins/pypi/micromamba.py,sha256=67FiIZZz0Kig9EcN7bZLObsE6Z1MFyo4Dp93fd3Grcc,12178
|
297
298
|
metaflow/plugins/pypi/pip.py,sha256=7B06mPOs5MvY33xbzPVYZlBr1iKMYaN-n8uulL9zSVg,13649
|
@@ -345,9 +346,9 @@ metaflow/tutorials/07-worldview/README.md,sha256=5vQTrFqulJ7rWN6r20dhot9lI2sVj9W
|
|
345
346
|
metaflow/tutorials/07-worldview/worldview.ipynb,sha256=ztPZPI9BXxvW1QdS2Tfe7LBuVzvFvv0AToDnsDJhLdE,2237
|
346
347
|
metaflow/tutorials/08-autopilot/README.md,sha256=GnePFp_q76jPs991lMUqfIIh5zSorIeWznyiUxzeUVE,1039
|
347
348
|
metaflow/tutorials/08-autopilot/autopilot.ipynb,sha256=DQoJlILV7Mq9vfPBGW-QV_kNhWPjS5n6SJLqePjFYLY,3191
|
348
|
-
metaflow-2.12.
|
349
|
-
metaflow-2.12.
|
350
|
-
metaflow-2.12.
|
351
|
-
metaflow-2.12.
|
352
|
-
metaflow-2.12.
|
353
|
-
metaflow-2.12.
|
349
|
+
metaflow-2.12.21.dist-info/LICENSE,sha256=nl_Lt5v9VvJ-5lWJDT4ddKAG-VZ-2IaLmbzpgYDz2hU,11343
|
350
|
+
metaflow-2.12.21.dist-info/METADATA,sha256=LjqXVil716Z6tVFMNHC8OOr1CZ4FeBarzBRiZdPlpzM,5906
|
351
|
+
metaflow-2.12.21.dist-info/WHEEL,sha256=AHX6tWk3qWuce7vKLrj7lnulVHEdWoltgauo8bgCXgU,109
|
352
|
+
metaflow-2.12.21.dist-info/entry_points.txt,sha256=IKwTN1T3I5eJL3uo_vnkyxVffcgnRdFbKwlghZfn27k,57
|
353
|
+
metaflow-2.12.21.dist-info/top_level.txt,sha256=v1pDHoWaSaKeuc5fKTRSfsXCKSdW1zvNVmvA-i0if3o,9
|
354
|
+
metaflow-2.12.21.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|