outerbounds 0.3.55rc3__py3-none-any.whl → 0.3.133__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.
- outerbounds/_vendor/PyYAML.LICENSE +20 -0
- outerbounds/_vendor/__init__.py +0 -0
- outerbounds/_vendor/_yaml/__init__.py +34 -0
- outerbounds/_vendor/click/__init__.py +73 -0
- outerbounds/_vendor/click/_compat.py +626 -0
- outerbounds/_vendor/click/_termui_impl.py +717 -0
- outerbounds/_vendor/click/_textwrap.py +49 -0
- outerbounds/_vendor/click/_winconsole.py +279 -0
- outerbounds/_vendor/click/core.py +2998 -0
- outerbounds/_vendor/click/decorators.py +497 -0
- outerbounds/_vendor/click/exceptions.py +287 -0
- outerbounds/_vendor/click/formatting.py +301 -0
- outerbounds/_vendor/click/globals.py +68 -0
- outerbounds/_vendor/click/parser.py +529 -0
- outerbounds/_vendor/click/py.typed +0 -0
- outerbounds/_vendor/click/shell_completion.py +580 -0
- outerbounds/_vendor/click/termui.py +787 -0
- outerbounds/_vendor/click/testing.py +479 -0
- outerbounds/_vendor/click/types.py +1073 -0
- outerbounds/_vendor/click/utils.py +580 -0
- outerbounds/_vendor/click.LICENSE +28 -0
- outerbounds/_vendor/vendor_any.txt +2 -0
- outerbounds/_vendor/yaml/__init__.py +471 -0
- outerbounds/_vendor/yaml/_yaml.cpython-311-darwin.so +0 -0
- outerbounds/_vendor/yaml/composer.py +146 -0
- outerbounds/_vendor/yaml/constructor.py +862 -0
- outerbounds/_vendor/yaml/cyaml.py +177 -0
- outerbounds/_vendor/yaml/dumper.py +138 -0
- outerbounds/_vendor/yaml/emitter.py +1239 -0
- outerbounds/_vendor/yaml/error.py +94 -0
- outerbounds/_vendor/yaml/events.py +104 -0
- outerbounds/_vendor/yaml/loader.py +62 -0
- outerbounds/_vendor/yaml/nodes.py +51 -0
- outerbounds/_vendor/yaml/parser.py +629 -0
- outerbounds/_vendor/yaml/reader.py +208 -0
- outerbounds/_vendor/yaml/representer.py +378 -0
- outerbounds/_vendor/yaml/resolver.py +245 -0
- outerbounds/_vendor/yaml/scanner.py +1555 -0
- outerbounds/_vendor/yaml/serializer.py +127 -0
- outerbounds/_vendor/yaml/tokens.py +129 -0
- outerbounds/command_groups/apps_cli.py +450 -0
- outerbounds/command_groups/cli.py +9 -5
- outerbounds/command_groups/local_setup_cli.py +249 -33
- outerbounds/command_groups/perimeters_cli.py +231 -33
- outerbounds/command_groups/tutorials_cli.py +111 -0
- outerbounds/command_groups/workstations_cli.py +88 -15
- outerbounds/utils/kubeconfig.py +2 -2
- outerbounds/utils/metaflowconfig.py +111 -21
- outerbounds/utils/schema.py +8 -2
- outerbounds/utils/utils.py +19 -0
- outerbounds/vendor.py +159 -0
- {outerbounds-0.3.55rc3.dist-info → outerbounds-0.3.133.dist-info}/METADATA +17 -6
- outerbounds-0.3.133.dist-info/RECORD +59 -0
- {outerbounds-0.3.55rc3.dist-info → outerbounds-0.3.133.dist-info}/WHEEL +1 -1
- outerbounds-0.3.55rc3.dist-info/RECORD +0 -15
- {outerbounds-0.3.55rc3.dist-info → outerbounds-0.3.133.dist-info}/entry_points.txt +0 -0
| @@ -1,17 +1,66 @@ | |
| 1 | 
            +
            from outerbounds._vendor import click
         | 
| 1 2 | 
             
            import json
         | 
| 2 3 | 
             
            import os
         | 
| 3 4 | 
             
            import requests
         | 
| 4 5 | 
             
            from os import path
         | 
| 5 | 
            -
            import  | 
| 6 | 
            +
            from typing import Dict, Union
         | 
| 7 | 
            +
            import sys
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            """
         | 
| 10 | 
            +
            key: perimeter specific URL to fetch the remote metaflow config from
         | 
| 11 | 
            +
            value: the remote metaflow config
         | 
| 12 | 
            +
            """
         | 
| 13 | 
            +
            CACHED_REMOTE_METAFLOW_CONFIG: Dict[str, Dict[str, str]] = {}
         | 
| 14 | 
            +
             | 
| 15 | 
            +
             | 
| 16 | 
            +
            CURRENT_PERIMETER_KEY = "OB_CURRENT_PERIMETER"
         | 
| 17 | 
            +
            CURRENT_PERIMETER_URL = "OB_CURRENT_PERIMETER_MF_CONFIG_URL"
         | 
| 18 | 
            +
            CURRENT_PERIMETER_URL_LEGACY_KEY = (
         | 
| 19 | 
            +
                "OB_CURRENT_PERIMETER_URL"  # For backwards compatibility with workstations.
         | 
| 20 | 
            +
            )
         | 
| 21 | 
            +
             | 
| 22 | 
            +
             | 
| 23 | 
            +
            def init_config(config_dir, profile) -> Dict[str, str]:
         | 
| 24 | 
            +
                global CACHED_REMOTE_METAFLOW_CONFIG
         | 
| 25 | 
            +
                config = read_metaflow_config_from_filesystem(config_dir, profile)
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                # Either user has an ob_config.json file with the perimeter URL
         | 
| 28 | 
            +
                # or the default config on the filesystem has the config URL in it.
         | 
| 29 | 
            +
                perimeter_specifc_url = get_perimeter_config_url_if_set_in_ob_config(
         | 
| 30 | 
            +
                    config_dir, profile
         | 
| 31 | 
            +
                ) or config.get("OBP_METAFLOW_CONFIG_URL", "")
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                if perimeter_specifc_url != "":
         | 
| 34 | 
            +
                    if perimeter_specifc_url in CACHED_REMOTE_METAFLOW_CONFIG:
         | 
| 35 | 
            +
                        return CACHED_REMOTE_METAFLOW_CONFIG[perimeter_specifc_url]
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    remote_config = init_config_from_url(config_dir, profile, perimeter_specifc_url)
         | 
| 38 | 
            +
                    remote_config["OBP_METAFLOW_CONFIG_URL"] = perimeter_specifc_url
         | 
| 6 39 |  | 
| 40 | 
            +
                    CACHED_REMOTE_METAFLOW_CONFIG[perimeter_specifc_url] = remote_config
         | 
| 41 | 
            +
                    return remote_config
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                return config
         | 
| 44 | 
            +
             | 
| 45 | 
            +
             | 
| 46 | 
            +
            def init_config_from_url(config_dir, profile, url) -> Dict[str, str]:
         | 
| 47 | 
            +
                config = read_metaflow_config_from_filesystem(config_dir, profile)
         | 
| 7 48 |  | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 49 | 
            +
                if config is None or "METAFLOW_SERVICE_AUTH_KEY" not in config:
         | 
| 50 | 
            +
                    raise Exception("METAFLOW_SERVICE_AUTH_KEY not found in config file")
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                config_response = requests.get(
         | 
| 53 | 
            +
                    url,
         | 
| 54 | 
            +
                    headers={"x-api-key": f'{config["METAFLOW_SERVICE_AUTH_KEY"]}'},
         | 
| 12 55 | 
             
                )
         | 
| 56 | 
            +
                config_response.raise_for_status()
         | 
| 57 | 
            +
                remote_config = config_response.json()["config"]
         | 
| 58 | 
            +
                return remote_config
         | 
| 59 | 
            +
             | 
| 13 60 |  | 
| 61 | 
            +
            def read_metaflow_config_from_filesystem(config_dir, profile) -> Dict[str, str]:
         | 
| 14 62 | 
             
                config_filename = f"config_{profile}.json" if profile else "config.json"
         | 
| 63 | 
            +
             | 
| 15 64 | 
             
                path_to_config = os.path.join(config_dir, config_filename)
         | 
| 16 65 |  | 
| 17 66 | 
             
                if os.path.exists(path_to_config):
         | 
| @@ -19,22 +68,6 @@ def init_config(config_dir="", profile="") -> dict: | |
| 19 68 | 
             
                        config = json.load(json_file)
         | 
| 20 69 | 
             
                else:
         | 
| 21 70 | 
             
                    raise Exception("Unable to locate metaflow config at '%s')" % (path_to_config))
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                # This is new remote-metaflow config; fetch it from the URL
         | 
| 24 | 
            -
                if "OBP_METAFLOW_CONFIG_URL" in config:
         | 
| 25 | 
            -
                    if config is None or "METAFLOW_SERVICE_AUTH_KEY" not in config:
         | 
| 26 | 
            -
                        raise Exception("METAFLOW_SERVICE_AUTH_KEY not found in config file")
         | 
| 27 | 
            -
             | 
| 28 | 
            -
                    config_response = requests.get(
         | 
| 29 | 
            -
                        config["OBP_METAFLOW_CONFIG_URL"],
         | 
| 30 | 
            -
                        headers={"x-api-key": f'{config["METAFLOW_SERVICE_AUTH_KEY"]}'},
         | 
| 31 | 
            -
                    )
         | 
| 32 | 
            -
                    config_response.raise_for_status()
         | 
| 33 | 
            -
                    remote_config = config_response.json()["config"]
         | 
| 34 | 
            -
                    remote_config["METAFLOW_SERVICE_AUTH_KEY"] = config["METAFLOW_SERVICE_AUTH_KEY"]
         | 
| 35 | 
            -
                    return remote_config
         | 
| 36 | 
            -
             | 
| 37 | 
            -
                # Legacy config, use from filesystem
         | 
| 38 71 | 
             
                return config
         | 
| 39 72 |  | 
| 40 73 |  | 
| @@ -70,3 +103,60 @@ def get_sanitized_url_from_config(config_dir: str, profile: str, key: str) -> st | |
| 70 103 |  | 
| 71 104 | 
             
                url_in_config = url_in_config.rstrip("/")
         | 
| 72 105 | 
             
                return url_in_config
         | 
| 106 | 
            +
             | 
| 107 | 
            +
             | 
| 108 | 
            +
            def get_remote_metaflow_config_for_perimeter(
         | 
| 109 | 
            +
                origin_token: str, perimeter: str, api_server: str
         | 
| 110 | 
            +
            ):
         | 
| 111 | 
            +
                try:
         | 
| 112 | 
            +
                    response = requests.get(
         | 
| 113 | 
            +
                        f"{api_server}/v1/perimeters/{perimeter}/metaflowconfigs/default",
         | 
| 114 | 
            +
                        headers={"x-api-key": origin_token},
         | 
| 115 | 
            +
                    )
         | 
| 116 | 
            +
                    response.raise_for_status()
         | 
| 117 | 
            +
                    config = response.json()["config"]
         | 
| 118 | 
            +
                    config["METAFLOW_SERVICE_AUTH_KEY"] = origin_token
         | 
| 119 | 
            +
                    return config
         | 
| 120 | 
            +
                except Exception as e:
         | 
| 121 | 
            +
                    click.secho(
         | 
| 122 | 
            +
                        f"Failed to get metaflow config from {api_server}. Error: {str(e)}",
         | 
| 123 | 
            +
                        fg="red",
         | 
| 124 | 
            +
                    )
         | 
| 125 | 
            +
                    sys.exit(1)
         | 
| 126 | 
            +
             | 
| 127 | 
            +
             | 
| 128 | 
            +
            def get_ob_config_file_path(config_dir: str, profile: str) -> str:
         | 
| 129 | 
            +
                # If OBP_CONFIG_DIR is set, use that, otherwise use METAFLOW_HOME
         | 
| 130 | 
            +
                # If neither are set, use ~/.metaflowconfig
         | 
| 131 | 
            +
                obp_config_dir = path.expanduser(os.environ.get("OBP_CONFIG_DIR", config_dir))
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                ob_config_filename = f"ob_config_{profile}.json" if profile else "ob_config.json"
         | 
| 134 | 
            +
                return os.path.expanduser(os.path.join(obp_config_dir, ob_config_filename))
         | 
| 135 | 
            +
             | 
| 136 | 
            +
             | 
| 137 | 
            +
            def get_perimeter_config_url_if_set_in_ob_config(
         | 
| 138 | 
            +
                config_dir: str, profile: str
         | 
| 139 | 
            +
            ) -> Union[str, None]:
         | 
| 140 | 
            +
                file_path = get_ob_config_file_path(config_dir, profile)
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                if os.path.exists(file_path):
         | 
| 143 | 
            +
                    with open(file_path, "r") as f:
         | 
| 144 | 
            +
                        ob_config = json.loads(f.read())
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                    if CURRENT_PERIMETER_URL in ob_config:
         | 
| 147 | 
            +
                        return ob_config[CURRENT_PERIMETER_URL]
         | 
| 148 | 
            +
                    elif CURRENT_PERIMETER_URL_LEGACY_KEY in ob_config:
         | 
| 149 | 
            +
                        return ob_config[CURRENT_PERIMETER_URL_LEGACY_KEY]
         | 
| 150 | 
            +
                    else:
         | 
| 151 | 
            +
                        raise ValueError(
         | 
| 152 | 
            +
                            "{} does not contain the key {}".format(
         | 
| 153 | 
            +
                                file_path, CURRENT_PERIMETER_KEY
         | 
| 154 | 
            +
                            )
         | 
| 155 | 
            +
                        )
         | 
| 156 | 
            +
                elif "OBP_CONFIG_DIR" in os.environ:
         | 
| 157 | 
            +
                    raise FileNotFoundError(
         | 
| 158 | 
            +
                        "Environment variable OBP_CONFIG_DIR is set to {} but this directory does not contain an ob_config.json file.".format(
         | 
| 159 | 
            +
                            os.environ["OBP_CONFIG_DIR"]
         | 
| 160 | 
            +
                        )
         | 
| 161 | 
            +
                    )
         | 
| 162 | 
            +
                return None
         | 
    
        outerbounds/utils/schema.py
    CHANGED
    
    | @@ -5,6 +5,7 @@ class OuterboundsCommandStatus(Enum): | |
| 5 5 | 
             
                OK = "OK"
         | 
| 6 6 | 
             
                FAIL = "FAIL"
         | 
| 7 7 | 
             
                WARN = "WARN"
         | 
| 8 | 
            +
                NOT_SUPPORTED = "NOT_SUPPORTED"
         | 
| 8 9 |  | 
| 9 10 |  | 
| 10 11 | 
             
            class CommandStatus:
         | 
| @@ -39,6 +40,11 @@ class OuterboundsCommandResponse: | |
| 39 40 | 
             
                    self.metadata = {}
         | 
| 40 41 | 
             
                    self._data = {}
         | 
| 41 42 |  | 
| 43 | 
            +
                def update(self, status, code, message):
         | 
| 44 | 
            +
                    self.status = status
         | 
| 45 | 
            +
                    self._code = code
         | 
| 46 | 
            +
                    self._message = message
         | 
| 47 | 
            +
             | 
| 42 48 | 
             
                def add_or_update_metadata(self, key, value):
         | 
| 43 49 | 
             
                    self.metadata[key] = value
         | 
| 44 50 |  | 
| @@ -53,14 +59,14 @@ class OuterboundsCommandResponse: | |
| 53 59 | 
             
                    if step.status == OuterboundsCommandStatus.FAIL:
         | 
| 54 60 | 
             
                        self.status = OuterboundsCommandStatus.FAIL
         | 
| 55 61 | 
             
                        self._code = 500
         | 
| 56 | 
            -
                        self._message = " | 
| 62 | 
            +
                        self._message = "Encountered an error when trying to run command."
         | 
| 57 63 | 
             
                    elif (
         | 
| 58 64 | 
             
                        step.status == OuterboundsCommandStatus.WARN
         | 
| 59 65 | 
             
                        and self.status != OuterboundsCommandStatus.FAIL
         | 
| 60 66 | 
             
                    ):
         | 
| 61 67 | 
             
                        self.status = OuterboundsCommandStatus.WARN
         | 
| 62 68 | 
             
                        self._code = 200
         | 
| 63 | 
            -
                        self._message = " | 
| 69 | 
            +
                        self._message = "Encountered one or more warnings when running the command."
         | 
| 64 70 |  | 
| 65 71 | 
             
                def as_dict(self):
         | 
| 66 72 | 
             
                    self._data["steps"] = [step.as_dict() for step in self._steps]
         | 
| @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            import tempfile
         | 
| 2 | 
            +
            import os
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            """
         | 
| 5 | 
            +
            Writes the given data to file_loc. Ensures that the directory exists
         | 
| 6 | 
            +
            and uses a temporary file to ensure that the file is written atomically.
         | 
| 7 | 
            +
            """
         | 
| 8 | 
            +
             | 
| 9 | 
            +
             | 
| 10 | 
            +
            def safe_write_to_disk(file_loc, data):
         | 
| 11 | 
            +
                # Ensure the directory exists
         | 
| 12 | 
            +
                os.makedirs(os.path.dirname(file_loc), exist_ok=True)
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                with tempfile.NamedTemporaryFile(
         | 
| 15 | 
            +
                    "w", dir=os.path.dirname(file_loc), delete=False
         | 
| 16 | 
            +
                ) as f:
         | 
| 17 | 
            +
                    f.write(data)
         | 
| 18 | 
            +
                    tmp_file = f.name
         | 
| 19 | 
            +
                os.rename(tmp_file, file_loc)
         | 
    
        outerbounds/vendor.py
    ADDED
    
    | @@ -0,0 +1,159 @@ | |
| 1 | 
            +
            import glob
         | 
| 2 | 
            +
            import shutil
         | 
| 3 | 
            +
            import subprocess
         | 
| 4 | 
            +
            import re
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            from functools import partial
         | 
| 7 | 
            +
            from itertools import chain
         | 
| 8 | 
            +
            from pathlib import Path
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            WHITELIST = {
         | 
| 11 | 
            +
                "README.txt",
         | 
| 12 | 
            +
                "__init__.py",
         | 
| 13 | 
            +
                "vendor_any.txt",
         | 
| 14 | 
            +
                "pip.LICENSE",
         | 
| 15 | 
            +
            }
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            # Borrowed from https://github.com/pypa/pip/tree/main/src/pip/_vendor
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            VENDOR_SUBDIR = re.compile(r"^_vendor/vendor_([a-zA-Z0-9_]+).txt$")
         | 
| 20 | 
            +
             | 
| 21 | 
            +
             | 
| 22 | 
            +
            def delete_all(*paths, whitelist=frozenset()):
         | 
| 23 | 
            +
                for item in paths:
         | 
| 24 | 
            +
                    if item.is_dir():
         | 
| 25 | 
            +
                        shutil.rmtree(item, ignore_errors=True)
         | 
| 26 | 
            +
                    elif item.is_file() and item.name not in whitelist:
         | 
| 27 | 
            +
                        item.unlink()
         | 
| 28 | 
            +
             | 
| 29 | 
            +
             | 
| 30 | 
            +
            def iter_subtree(path):
         | 
| 31 | 
            +
                """Recursively yield all files in a subtree, depth-first"""
         | 
| 32 | 
            +
                if not path.is_dir():
         | 
| 33 | 
            +
                    if path.is_file():
         | 
| 34 | 
            +
                        yield path
         | 
| 35 | 
            +
                    return
         | 
| 36 | 
            +
                for item in path.iterdir():
         | 
| 37 | 
            +
                    if item.is_dir():
         | 
| 38 | 
            +
                        yield from iter_subtree(item)
         | 
| 39 | 
            +
                    elif item.is_file():
         | 
| 40 | 
            +
                        yield item
         | 
| 41 | 
            +
             | 
| 42 | 
            +
             | 
| 43 | 
            +
            def patch_vendor_imports(file, replacements):
         | 
| 44 | 
            +
                text = file.read_text("utf8")
         | 
| 45 | 
            +
                for replacement in replacements:
         | 
| 46 | 
            +
                    text = replacement(text)
         | 
| 47 | 
            +
                file.write_text(text, "utf8")
         | 
| 48 | 
            +
             | 
| 49 | 
            +
             | 
| 50 | 
            +
            def find_vendored_libs(vendor_dir, whitelist, whitelist_dirs):
         | 
| 51 | 
            +
                vendored_libs = []
         | 
| 52 | 
            +
                paths = []
         | 
| 53 | 
            +
                for item in vendor_dir.iterdir():
         | 
| 54 | 
            +
                    if item.is_dir() and item not in whitelist_dirs:
         | 
| 55 | 
            +
                        vendored_libs.append(item.name)
         | 
| 56 | 
            +
                    elif item.is_file() and item.name not in whitelist:
         | 
| 57 | 
            +
                        vendored_libs.append(item.stem)  # without extension
         | 
| 58 | 
            +
                    else:  # not a dir or a file not in the whitelist
         | 
| 59 | 
            +
                        continue
         | 
| 60 | 
            +
                    paths.append(item)
         | 
| 61 | 
            +
                return vendored_libs, paths
         | 
| 62 | 
            +
             | 
| 63 | 
            +
             | 
| 64 | 
            +
            def fetch_licenses(*info_dir, vendor_dir):
         | 
| 65 | 
            +
                for file in chain.from_iterable(map(iter_subtree, info_dir)):
         | 
| 66 | 
            +
                    if "LICENSE" in file.name:
         | 
| 67 | 
            +
                        library = file.parent.name.split("-")[0]
         | 
| 68 | 
            +
                        shutil.copy(file, vendor_dir / ("%s.LICENSE" % library))
         | 
| 69 | 
            +
                    else:
         | 
| 70 | 
            +
                        continue
         | 
| 71 | 
            +
             | 
| 72 | 
            +
             | 
| 73 | 
            +
            def vendor(vendor_dir):
         | 
| 74 | 
            +
                # remove everything
         | 
| 75 | 
            +
                delete_all(*vendor_dir.iterdir(), whitelist=WHITELIST)
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                exclude_subdirs = []
         | 
| 78 | 
            +
                # Iterate on the vendor*.txt files
         | 
| 79 | 
            +
                for vendor_file in glob.glob(f"{vendor_dir.name}/vendor*.txt"):
         | 
| 80 | 
            +
                    # We extract the subdirectory we are going to extract into
         | 
| 81 | 
            +
                    subdir = VENDOR_SUBDIR.match(vendor_file).group(1)
         | 
| 82 | 
            +
                    # Includes "any" but it doesn't really matter unless you install "any"
         | 
| 83 | 
            +
                    exclude_subdirs.append(subdir)
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                for subdir in exclude_subdirs:
         | 
| 86 | 
            +
                    create_init_file = False
         | 
| 87 | 
            +
                    if subdir == "any":
         | 
| 88 | 
            +
                        vendor_subdir = vendor_dir
         | 
| 89 | 
            +
                        # target package is <parent>.<vendor_dir>; foo/_vendor -> foo._vendor
         | 
| 90 | 
            +
                        pkgname = f"{vendor_dir.parent.name}.{vendor_dir.name}"
         | 
| 91 | 
            +
                    else:
         | 
| 92 | 
            +
                        create_init_file = True
         | 
| 93 | 
            +
                        vendor_subdir = vendor_dir / subdir
         | 
| 94 | 
            +
                        # target package is <parent>.<vendor_dir>; foo/_vendor -> foo._vendor
         | 
| 95 | 
            +
                        pkgname = f"{vendor_dir.parent.name}.{vendor_dir.name}.{vendor_subdir.name}"
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                    # install with pip
         | 
| 98 | 
            +
                    subprocess.run(
         | 
| 99 | 
            +
                        [
         | 
| 100 | 
            +
                            "python3",
         | 
| 101 | 
            +
                            "-m",
         | 
| 102 | 
            +
                            "pip",
         | 
| 103 | 
            +
                            "install",
         | 
| 104 | 
            +
                            "-t",
         | 
| 105 | 
            +
                            str(vendor_subdir),
         | 
| 106 | 
            +
                            "-r",
         | 
| 107 | 
            +
                            "_vendor/vendor_%s.txt" % subdir,
         | 
| 108 | 
            +
                            "--no-compile",
         | 
| 109 | 
            +
                        ]
         | 
| 110 | 
            +
                    )
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                    # fetch licenses
         | 
| 113 | 
            +
                    fetch_licenses(*vendor_subdir.glob("*.dist-info"), vendor_dir=vendor_subdir)
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                    # delete stuff that's not needed
         | 
| 116 | 
            +
                    delete_all(
         | 
| 117 | 
            +
                        *vendor_subdir.glob("*.dist-info"),
         | 
| 118 | 
            +
                        *vendor_subdir.glob("*.egg-info"),
         | 
| 119 | 
            +
                        vendor_subdir / "bin",
         | 
| 120 | 
            +
                    )
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                    # Touch a __init__.py file
         | 
| 123 | 
            +
                    if create_init_file:
         | 
| 124 | 
            +
                        with open(
         | 
| 125 | 
            +
                            "%s/__init__.py" % str(vendor_subdir), "w+", encoding="utf-8"
         | 
| 126 | 
            +
                        ) as f:
         | 
| 127 | 
            +
                            f.write("# Empty file")
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                    vendored_libs, paths = find_vendored_libs(
         | 
| 130 | 
            +
                        vendor_subdir, WHITELIST, exclude_subdirs
         | 
| 131 | 
            +
                    )
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                    replacements = []
         | 
| 134 | 
            +
                    for lib in vendored_libs:
         | 
| 135 | 
            +
                        replacements += (
         | 
| 136 | 
            +
                            partial(  # import bar -> import foo._vendor.bar
         | 
| 137 | 
            +
                                re.compile(r"(^\s*)import {}\n".format(lib), flags=re.M).sub,
         | 
| 138 | 
            +
                                r"\1from {} import {}\n".format(pkgname, lib),
         | 
| 139 | 
            +
                            ),
         | 
| 140 | 
            +
                            partial(  # from bar -> from foo._vendor.bar
         | 
| 141 | 
            +
                                re.compile(r"(^\s*)from {}(\.|\s+)".format(lib), flags=re.M).sub,
         | 
| 142 | 
            +
                                r"\1from {}.{}\2".format(pkgname, lib),
         | 
| 143 | 
            +
                            ),
         | 
| 144 | 
            +
                        )
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                    for file in chain.from_iterable(map(iter_subtree, paths)):
         | 
| 147 | 
            +
                        if file.suffix == ".py":
         | 
| 148 | 
            +
                            patch_vendor_imports(file, replacements)
         | 
| 149 | 
            +
             | 
| 150 | 
            +
             | 
| 151 | 
            +
            if __name__ == "__main__":
         | 
| 152 | 
            +
                here = Path("__file__").resolve().parent
         | 
| 153 | 
            +
                vendor_tl_dir = here / "_vendor"
         | 
| 154 | 
            +
                has_vendor_file = len(glob.glob(f"{vendor_tl_dir.name}/vendor*.txt")) > 0
         | 
| 155 | 
            +
                assert has_vendor_file, "_vendor/vendor*.txt file not found"
         | 
| 156 | 
            +
                assert (
         | 
| 157 | 
            +
                    vendor_tl_dir / "__init__.py"
         | 
| 158 | 
            +
                ).exists(), "_vendor/__init__.py file not found"
         | 
| 159 | 
            +
                vendor(vendor_tl_dir)
         | 
| @@ -1,31 +1,42 @@ | |
| 1 1 | 
             
            Metadata-Version: 2.1
         | 
| 2 2 | 
             
            Name: outerbounds
         | 
| 3 | 
            -
            Version: 0.3. | 
| 3 | 
            +
            Version: 0.3.133
         | 
| 4 4 | 
             
            Summary: More Data Science, Less Administration
         | 
| 5 5 | 
             
            License: Proprietary
         | 
| 6 6 | 
             
            Keywords: data science,machine learning,MLOps
         | 
| 7 7 | 
             
            Author: Outerbounds, Inc.
         | 
| 8 | 
            -
            Requires-Python: >=3. | 
| 8 | 
            +
            Requires-Python: >=3.7,<4.0
         | 
| 9 9 | 
             
            Classifier: Development Status :: 4 - Beta
         | 
| 10 10 | 
             
            Classifier: License :: Other/Proprietary License
         | 
| 11 11 | 
             
            Classifier: Programming Language :: Python :: 3
         | 
| 12 | 
            +
            Classifier: Programming Language :: Python :: 3.7
         | 
| 12 13 | 
             
            Classifier: Programming Language :: Python :: 3.8
         | 
| 13 14 | 
             
            Classifier: Programming Language :: Python :: 3.9
         | 
| 14 15 | 
             
            Classifier: Programming Language :: Python :: 3.10
         | 
| 15 16 | 
             
            Classifier: Programming Language :: Python :: 3.11
         | 
| 17 | 
            +
            Classifier: Programming Language :: Python :: 3.12
         | 
| 16 18 | 
             
            Provides-Extra: azure
         | 
| 17 19 | 
             
            Provides-Extra: gcp
         | 
| 18 | 
            -
             | 
| 20 | 
            +
            Provides-Extra: snowflake
         | 
| 19 21 | 
             
            Requires-Dist: azure-identity (>=1.15.0,<2.0.0) ; extra == "azure"
         | 
| 22 | 
            +
            Requires-Dist: azure-keyvault-secrets (>=4.7.0,<5.0.0) ; extra == "azure"
         | 
| 20 23 | 
             
            Requires-Dist: azure-storage-blob (>=12.9.0,<13.0.0) ; extra == "azure"
         | 
| 21 24 | 
             
            Requires-Dist: boto3
         | 
| 22 | 
            -
            Requires-Dist: click (>=8.1.3,<9.0.0)
         | 
| 23 25 | 
             
            Requires-Dist: google-api-core (>=2.16.1,<3.0.0) ; extra == "gcp"
         | 
| 24 26 | 
             
            Requires-Dist: google-auth (>=2.27.0,<3.0.0) ; extra == "gcp"
         | 
| 27 | 
            +
            Requires-Dist: google-cloud-secret-manager (>=2.20.0,<3.0.0) ; extra == "gcp"
         | 
| 25 28 | 
             
            Requires-Dist: google-cloud-storage (>=2.14.0,<3.0.0) ; extra == "gcp"
         | 
| 26 | 
            -
            Requires-Dist:  | 
| 27 | 
            -
            Requires-Dist: ob-metaflow | 
| 29 | 
            +
            Requires-Dist: metaflow-checkpoint (==0.1.6)
         | 
| 30 | 
            +
            Requires-Dist: ob-metaflow (==2.13.4.1)
         | 
| 31 | 
            +
            Requires-Dist: ob-metaflow-extensions (==1.1.121)
         | 
| 32 | 
            +
            Requires-Dist: ob-metaflow-stubs (==6.0.3.133)
         | 
| 28 33 | 
             
            Requires-Dist: opentelemetry-distro (==0.41b0)
         | 
| 29 34 | 
             
            Requires-Dist: opentelemetry-exporter-otlp-proto-http (==1.20.0)
         | 
| 30 35 | 
             
            Requires-Dist: opentelemetry-instrumentation-requests (==0.41b0)
         | 
| 31 36 | 
             
            Project-URL: Documentation, https://docs.metaflow.org
         | 
| 37 | 
            +
            Description-Content-Type: text/markdown
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            # Outerbounds
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            Main package for the Outerbounds platform.
         | 
| 42 | 
            +
             | 
| @@ -0,0 +1,59 @@ | |
| 1 | 
            +
            outerbounds/__init__.py,sha256=GPdaubvAYF8pOFWJ3b-sPMKCpyfpteWVMZWkmaYhxRw,32
         | 
| 2 | 
            +
            outerbounds/_vendor/PyYAML.LICENSE,sha256=jTko-dxEkP1jVwfLiOsmvXZBAqcoKVQwfT5RZ6V36KQ,1101
         | 
| 3 | 
            +
            outerbounds/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 4 | 
            +
            outerbounds/_vendor/_yaml/__init__.py,sha256=nD985-g4Mrx97PhtSzI2L53o8sCHUJ4ZoBWcUd7o0PQ,1449
         | 
| 5 | 
            +
            outerbounds/_vendor/click/__init__.py,sha256=rQBLutqg-z6m8nOzivIfigDn_emijB_dKv9BZ2FNi5s,3138
         | 
| 6 | 
            +
            outerbounds/_vendor/click/_compat.py,sha256=JIHLYs7Jzz4KT9t-ds4o4jBzLjnwCiJQKqur-5iwCKI,18810
         | 
| 7 | 
            +
            outerbounds/_vendor/click/_termui_impl.py,sha256=qK6Cfy4mRFxvxE8dya8RBhLpSC8HjF-lvBc6aNrPdwg,23451
         | 
| 8 | 
            +
            outerbounds/_vendor/click/_textwrap.py,sha256=10fQ64OcBUMuK7mFvh8363_uoOxPlRItZBmKzRJDgoY,1353
         | 
| 9 | 
            +
            outerbounds/_vendor/click/_winconsole.py,sha256=5ju3jQkcZD0W27WEMGqmEP4y_crUVzPCqsX_FYb7BO0,7860
         | 
| 10 | 
            +
            outerbounds/_vendor/click/core.py,sha256=ici4JXpq5VWyQkFQQklE4GnUKR8wxSP1YAzqaVYAZ3Y,112862
         | 
| 11 | 
            +
            outerbounds/_vendor/click/decorators.py,sha256=yo3zvzgUm5q7h5CXjyV6q3h_PJAiUaem178zXwdWUFI,16350
         | 
| 12 | 
            +
            outerbounds/_vendor/click/exceptions.py,sha256=7gDaLGuFZBeCNwY9ERMsF2-Z3R9Fvq09Zc6IZSKjseo,9167
         | 
| 13 | 
            +
            outerbounds/_vendor/click/formatting.py,sha256=Frf0-5W33-loyY_i9qrwXR8-STnW3m5gvyxLVUdyxyk,9706
         | 
| 14 | 
            +
            outerbounds/_vendor/click/globals.py,sha256=TP-qM88STzc7f127h35TD_v920FgfOD2EwzqA0oE8XU,1961
         | 
| 15 | 
            +
            outerbounds/_vendor/click/parser.py,sha256=cAEt1uQR8gq3-S9ysqbVU-fdAZNvilxw4ReJ_T1OQMk,19044
         | 
| 16 | 
            +
            outerbounds/_vendor/click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 17 | 
            +
            outerbounds/_vendor/click/shell_completion.py,sha256=qOp_BeC9esEOSZKyu5G7RIxEUaLsXUX-mTb7hB1r4QY,18018
         | 
| 18 | 
            +
            outerbounds/_vendor/click/termui.py,sha256=ACBQVOvFCTSqtD5VREeCAdRtlHd-Imla-Lte4wSfMjA,28355
         | 
| 19 | 
            +
            outerbounds/_vendor/click/testing.py,sha256=ptpMYgRY7dVfE3UDgkgwayu9ePw98sQI3D7zZXiCpj4,16063
         | 
| 20 | 
            +
            outerbounds/_vendor/click/types.py,sha256=u8LK2CRcVw3jWDutzP_wgFV478TXhsjL8gYSFPjGKkE,35865
         | 
| 21 | 
            +
            outerbounds/_vendor/click/utils.py,sha256=33D6E7poH_nrKB-xr-UyDEXnxOcCiQqxuRLtrqeVv6o,18682
         | 
| 22 | 
            +
            outerbounds/_vendor/click.LICENSE,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475
         | 
| 23 | 
            +
            outerbounds/_vendor/vendor_any.txt,sha256=9vi_h2zSBIx0sFM9i69xNEwe-HyeX0_sdtMjImmvgXo,27
         | 
| 24 | 
            +
            outerbounds/_vendor/yaml/__init__.py,sha256=lPcXUknB0EUNfCL8MJOgq26y70nOQR_Eajqzycmtnhg,12569
         | 
| 25 | 
            +
            outerbounds/_vendor/yaml/_yaml.cpython-311-darwin.so,sha256=YiF55JiadfOvw_mUH-lONNnsiMHj6C6o1SBfTCvvW54,362008
         | 
| 26 | 
            +
            outerbounds/_vendor/yaml/composer.py,sha256=KI1ASnRYW7jWza4BxiHsjDdpV_AYR6wGKD_d3q5y1GY,4975
         | 
| 27 | 
            +
            outerbounds/_vendor/yaml/constructor.py,sha256=a_QHcwgF8J3ius54IchRi50VSuX-tNZfL83JsRA9q9w,30058
         | 
| 28 | 
            +
            outerbounds/_vendor/yaml/cyaml.py,sha256=pbSaKr0mn_yMEc4DuLbl38RGWVSkXBgA9NXHATnjfuE,4497
         | 
| 29 | 
            +
            outerbounds/_vendor/yaml/dumper.py,sha256=ncn_DlBmKv_aTtkv9tBagK-8SQE9ETLIdwTYMO0_5WA,3507
         | 
| 30 | 
            +
            outerbounds/_vendor/yaml/emitter.py,sha256=9jcV2QKdsHWAPJrqsWMEBCLCU56D25qLC1XiQFcU48A,44167
         | 
| 31 | 
            +
            outerbounds/_vendor/yaml/error.py,sha256=1ImZXAcfUbnuVfkHYSeHakDojgwAjFs4pFUKfKNFLuw,2726
         | 
| 32 | 
            +
            outerbounds/_vendor/yaml/events.py,sha256=VlHDUFXu2zAU6TFEQUBDIq8gU3s_1Q67vpNlYY645p4,2465
         | 
| 33 | 
            +
            outerbounds/_vendor/yaml/loader.py,sha256=jX2YIC4HwJslE2DKVwLix2ne8qhJyFJzwjJz7CzVPOk,2060
         | 
| 34 | 
            +
            outerbounds/_vendor/yaml/nodes.py,sha256=K0QBIan-NI8RqxiPyo6fLaR-GSTpmF_9eXtJ3uf6rEo,1422
         | 
| 35 | 
            +
            outerbounds/_vendor/yaml/parser.py,sha256=BhbxGU0AjebJZIVFpBIPoP5YAGYOMVg7Vjmd-aFP5ts,26214
         | 
| 36 | 
            +
            outerbounds/_vendor/yaml/reader.py,sha256=lR8yfw6FMasPB41qFrsBUOVtASViXnKd89tvJHQNsVc,7109
         | 
| 37 | 
            +
            outerbounds/_vendor/yaml/representer.py,sha256=FK_yoIj_17GdgzVP8_yFBobV56PKqEkZ5AGtBdKjWDc,14088
         | 
| 38 | 
            +
            outerbounds/_vendor/yaml/resolver.py,sha256=dPhU1d7G1JCMktPFvNhyqwj2oNvx1yf_Jfa35CydQXA,8992
         | 
| 39 | 
            +
            outerbounds/_vendor/yaml/scanner.py,sha256=ZcI8IngR56PaQ0m27WU2vxCqmDCuRjz-hr7pirbMPuw,52982
         | 
| 40 | 
            +
            outerbounds/_vendor/yaml/serializer.py,sha256=8wFZRy9SsQSktF_f9OOroroqsh4qVUe53ry07P9UgCc,4368
         | 
| 41 | 
            +
            outerbounds/_vendor/yaml/tokens.py,sha256=JBSu38wihGr4l73JwbfMA7Ks1-X84g8-NskTz7KwPmA,2578
         | 
| 42 | 
            +
            outerbounds/cli_main.py,sha256=e9UMnPysmc7gbrimq2I4KfltggyU7pw59Cn9aEguVcU,74
         | 
| 43 | 
            +
            outerbounds/command_groups/__init__.py,sha256=QPWtj5wDRTINDxVUL7XPqG3HoxHNvYOg08EnuSZB2Hc,21
         | 
| 44 | 
            +
            outerbounds/command_groups/apps_cli.py,sha256=8jmQufa0bK2sfRfs7DiWjoJ1oWiqZAixsL4Dte_KY4Y,17201
         | 
| 45 | 
            +
            outerbounds/command_groups/cli.py,sha256=q0hdJO4biD3iEOdyJcxnRkeleA8AKAhx842kQ49I6kk,365
         | 
| 46 | 
            +
            outerbounds/command_groups/local_setup_cli.py,sha256=tuuqJRXQ_guEwOuQSIf9wkUU0yg8yAs31myGViAK15s,36364
         | 
| 47 | 
            +
            outerbounds/command_groups/perimeters_cli.py,sha256=iF_Uw7ROiSctf6FgoJEy30iDBLVE1j9FKuR3shgJRmc,19050
         | 
| 48 | 
            +
            outerbounds/command_groups/tutorials_cli.py,sha256=UInFyiMqtscHFfi8YQwiY_6Sdw9quJOtRu5OukEBccw,3522
         | 
| 49 | 
            +
            outerbounds/command_groups/workstations_cli.py,sha256=V5Jbj1cVb4IRllI7fOgNgL6OekRpuFDv6CEhDb4xC6w,22016
         | 
| 50 | 
            +
            outerbounds/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 51 | 
            +
            outerbounds/utils/kubeconfig.py,sha256=yvcyRXGR4AhQuqUDqmbGxEOHw5ixMFV0AZIDg1LI_Qo,7981
         | 
| 52 | 
            +
            outerbounds/utils/metaflowconfig.py,sha256=l2vJbgPkLISU-XPGZFaC8ZKmYFyJemlD6bwB-EKUsAw,5770
         | 
| 53 | 
            +
            outerbounds/utils/schema.py,sha256=lMUr9kNgn9wy-sO_t_Tlxmbt63yLeN4b0xQXbDUDj4A,2331
         | 
| 54 | 
            +
            outerbounds/utils/utils.py,sha256=4Z8cszNob_8kDYCLNTrP-wWads_S_MdL3Uj3ju4mEsk,501
         | 
| 55 | 
            +
            outerbounds/vendor.py,sha256=gRLRJNXtZBeUpPEog0LOeIsl6GosaFFbCxUvR4bW6IQ,5093
         | 
| 56 | 
            +
            outerbounds-0.3.133.dist-info/METADATA,sha256=q0HvciFfYuFHoyzBF7-2hnU7EkQIk47mtcxUOxtuWKs,1761
         | 
| 57 | 
            +
            outerbounds-0.3.133.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
         | 
| 58 | 
            +
            outerbounds-0.3.133.dist-info/entry_points.txt,sha256=7ye0281PKlvqxu15rjw60zKg2pMsXI49_A8BmGqIqBw,47
         | 
| 59 | 
            +
            outerbounds-0.3.133.dist-info/RECORD,,
         | 
| @@ -1,15 +0,0 @@ | |
| 1 | 
            -
            outerbounds/__init__.py,sha256=GPdaubvAYF8pOFWJ3b-sPMKCpyfpteWVMZWkmaYhxRw,32
         | 
| 2 | 
            -
            outerbounds/cli_main.py,sha256=e9UMnPysmc7gbrimq2I4KfltggyU7pw59Cn9aEguVcU,74
         | 
| 3 | 
            -
            outerbounds/command_groups/__init__.py,sha256=QPWtj5wDRTINDxVUL7XPqG3HoxHNvYOg08EnuSZB2Hc,21
         | 
| 4 | 
            -
            outerbounds/command_groups/cli.py,sha256=H4LxcYTmsY9DQUrReSRLjvbg9s9Ro7s-eUrcMqEJ_9A,261
         | 
| 5 | 
            -
            outerbounds/command_groups/local_setup_cli.py,sha256=0sEi3V0sqoCW0RI2z3xMLNnit0pCsigWQ07m1mhDBso,29524
         | 
| 6 | 
            -
            outerbounds/command_groups/perimeters_cli.py,sha256=9tOql42d00KfHpZYkLLGEAOiy8iRbIzsknldCyICwU0,12063
         | 
| 7 | 
            -
            outerbounds/command_groups/workstations_cli.py,sha256=f3gwHMZPHzeOcGj5VfC5tZZA18JQhFzy2LRGzqAosOk,19286
         | 
| 8 | 
            -
            outerbounds/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 9 | 
            -
            outerbounds/utils/kubeconfig.py,sha256=l1mUP1j9VIq3fsffi5bJ1Nk-hYlwd1dIqkpj7DvVS1E,7936
         | 
| 10 | 
            -
            outerbounds/utils/metaflowconfig.py,sha256=HgaDmK3F97rppfGUdysS1Zppe28ERTLV_HcB5IuPpV4,2631
         | 
| 11 | 
            -
            outerbounds/utils/schema.py,sha256=Ht_Yf5uoKO0m36WXHZLSPmWPH6EFWXfZDQsiAUquc5k,2160
         | 
| 12 | 
            -
            outerbounds-0.3.55rc3.dist-info/METADATA,sha256=ARhdx-T4KbMT1ri3AqRyA5vW4B_8dGHZGdRlcK-iBuI,1367
         | 
| 13 | 
            -
            outerbounds-0.3.55rc3.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
         | 
| 14 | 
            -
            outerbounds-0.3.55rc3.dist-info/entry_points.txt,sha256=7ye0281PKlvqxu15rjw60zKg2pMsXI49_A8BmGqIqBw,47
         | 
| 15 | 
            -
            outerbounds-0.3.55rc3.dist-info/RECORD,,
         | 
| 
            File without changes
         |