Skip to content

Commit adda9a9

Browse files
authored
Speed up shell and CLI invocation (#367)
* Experiment with speeding up CLI * Speed up CLI invocation further by late imports in cli itself * Remove __future__ annotations * Fix lint * Change completion script to not slow down shell start * CHANGELOG and bump to 0.14.24
1 parent d045384 commit adda9a9

16 files changed

+181
-125
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ All notable changes to the [Nucleus Python Client](https://github.com/scaleapi/n
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.14.24](https://github.com/scaleapi/nucleus-python-client/releases/tag/v0.14.24) - 2022-10-19
9+
10+
### Fixed
11+
- Late imports for seldomly used heavy libraries. Sped up CLI invocation and autocomplation.
12+
If you had shell completions installed before we recommend removeing them from your .(bash|zsh)rc
13+
file and reinstalling with nu install-completions
14+
815
## [0.14.23](https://github.com/scaleapi/nucleus-python-client/releases/tag/v0.14.23) - 2022-10-17
916

1017
### Added

cli/client.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
import functools
2-
import os
32

4-
import nucleus
3+
from rich.console import Console
54

65

76
@functools.lru_cache()
87
def init_client():
9-
api_key = os.environ.get("NUCLEUS_API_KEY", None)
10-
if api_key:
11-
client = nucleus.NucleusClient(api_key)
12-
else:
13-
raise RuntimeError("No NUCLEUS_API_KEY set")
14-
return client
8+
console = Console()
9+
with console.status("Initializing client"):
10+
import os
11+
12+
import nucleus
13+
14+
api_key = os.environ.get("NUCLEUS_API_KEY", None)
15+
if api_key:
16+
client = nucleus.NucleusClient(api_key)
17+
else:
18+
raise RuntimeError("No NUCLEUS_API_KEY set")
19+
return client

cli/install_completion.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,48 @@
11
import os
22
import shutil
3+
import subprocess
34

45
import click
56
from shellingham import detect_shell
67

78

9+
def generate_completions(shell, completion_path):
10+
command = f"_NU_COMPLETE={shell}_source nu > {completion_path}"
11+
subprocess.run(command, shell=True)
12+
click.echo(f"Generated completions for {shell}: {completion_path}")
13+
14+
815
@click.command("install-completion")
916
def install_completion():
1017
"""Install shell completion script to your rc file"""
1118
shell, _ = detect_shell()
19+
os.makedirs("~/.config", exist_ok=True)
20+
append_to_file = None
1221
if shell == "zsh":
1322
rc_path = "~/.zshrc"
14-
append_to_file = 'eval "$(_NU_COMPLETE=zsh_source nu)"'
23+
completion_path = "~/.config/nu-cli-completions.zsh"
24+
append_to_file = f". {completion_path}"
1525
elif shell == "bash":
1626
rc_path = "~/.bashrc"
17-
append_to_file = 'eval "$(_NU_COMPLETE=bash_source nu)"'
27+
completion_path = "~/.config/nu-cli-completions.bash"
28+
append_to_file = f". {completion_path}"
1829
elif shell == "fish":
1930
rc_path = "~/.config/fish/completions/foo-bar.fish"
20-
append_to_file = "eval (env _NU_COMPLETE=fish_source nu)"
31+
completion_path = "~/.config/fish/completions/nu.fish"
2132
else:
2233
raise RuntimeError(f"Unsupported shell {shell} for completions")
2334

24-
rc_path_expanded = os.path.expanduser(rc_path)
25-
rc_bak = f"{rc_path_expanded}.bak"
26-
shutil.copy(rc_path_expanded, rc_bak)
27-
click.echo(f"Backed up {rc_path} to {rc_bak}")
28-
with open(rc_path_expanded, mode="a") as rc_file:
29-
rc_file.write("\n")
30-
rc_file.write("# Shell completion for nu\n")
31-
rc_file.write(append_to_file)
35+
generate_completions(shell, completion_path)
36+
37+
if append_to_file is not None:
38+
rc_path_expanded = os.path.expanduser(rc_path)
39+
rc_bak = f"{rc_path_expanded}.bak"
40+
shutil.copy(rc_path_expanded, rc_bak)
41+
click.echo(f"Backed up {rc_path} to {rc_bak}")
42+
with open(rc_path_expanded, mode="a") as rc_file:
43+
rc_file.write("\n")
44+
rc_file.write("# Shell completion for nu\n")
45+
rc_file.write(append_to_file)
46+
3247
click.echo(f"Completion script added to {rc_path}")
3348
click.echo(f"Don't forget to `source {rc_path}")

cli/models.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import click
2-
import questionary
32
from rich.console import Console
43
from rich.pretty import pretty_repr
54
from rich.table import Column, Table
@@ -35,6 +34,8 @@ def json_string_to_string(s: str) -> str:
3534

3635
@models.command("calculate-metrics")
3736
def metrics():
37+
import questionary
38+
3839
client = init_client()
3940
models = client.models
4041
prompt_to_id = {f"{m.id}: {m.name}": m.id for m in models}

cli/slices.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
import click
22
from rich.console import Console
3-
from rich.live import Live
4-
from rich.spinner import Spinner
53
from rich.table import Column, Table
6-
from rich.tree import Tree
74

85
from cli.client import init_client
96
from cli.helpers.nucleus_url import nucleus_url
107
from cli.helpers.web_helper import launch_web_or_invoke
11-
from nucleus import NucleusAPIError
128

139

1410
@click.group("slices", invoke_without_command=True)

cli/tests.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import TYPE_CHECKING
2+
13
import click
24
from rich.console import Console
35
from rich.live import Live
@@ -7,12 +9,9 @@
79
from cli.client import init_client
810
from cli.helpers.nucleus_url import nucleus_url
911
from cli.helpers.web_helper import launch_web_or_invoke
10-
from nucleus import NucleusAPIError
11-
from nucleus.validate import (
12-
AvailableEvalFunctions,
13-
ScenarioTestMetric,
14-
ThresholdComparison,
15-
)
12+
13+
if TYPE_CHECKING:
14+
from nucleus.validate import AvailableEvalFunctions, ScenarioTestMetric
1615

1716

1817
@click.group("tests", invoke_without_command=True)
@@ -47,8 +46,10 @@ def list_tests():
4746

4847

4948
def format_criterion(
50-
criterion: ScenarioTestMetric, eval_functions: AvailableEvalFunctions
49+
criterion: "ScenarioTestMetric", eval_functions: "AvailableEvalFunctions"
5150
):
51+
from nucleus.validate import ThresholdComparison
52+
5253
op_map = {
5354
ThresholdComparison.GREATER_THAN: ">",
5455
ThresholdComparison.GREATER_THAN_EQUAL_TO: ">=",
@@ -95,6 +96,8 @@ def describe_test(scenario_test_id, all):
9596

9697

9798
def build_scenario_test_info_tree(client, scenario_test, tree):
99+
from nucleus import NucleusAPIError
100+
98101
try:
99102
slc = client.get_slice(scenario_test.slice_id)
100103
info_branch = tree.add(":mag: Details")

docs/conf.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import os
1414
import sys
1515

16+
import pkg_resources
17+
1618
sys.path.insert(0, os.path.abspath("../../"))
1719

1820

@@ -22,10 +24,9 @@
2224
copyright = "2021, Scale"
2325
author = "Scale"
2426

25-
# The full version, including alpha/beta/rc tags
26-
from nucleus import __version__ # noqa: E402
2727

28-
release = "v" + str(__version__)
28+
# The full version, including alpha/beta/rc tags
29+
release = "v" + str(pkg_resources.get_distribution("scale-nucleus").version)
2930

3031

3132
# -- General configuration ---------------------------------------------------

nucleus/__init__.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,9 @@
4444
import warnings
4545
from typing import Any, Dict, List, Optional, Sequence, Union
4646

47-
import pkg_resources
4847
import pydantic
4948
import requests
5049
import tqdm
51-
import tqdm.notebook as tqdm_notebook
5250

5351
from nucleus.url_utils import sanitize_string_args
5452

@@ -150,9 +148,6 @@
150148
# pylint: disable=C0302
151149

152150

153-
__version__ = pkg_resources.get_distribution("scale-nucleus").version
154-
155-
156151
class NucleusClient:
157152
"""Client to interact with the Nucleus API via Python SDK.
158153
@@ -180,6 +175,8 @@ def __init__(
180175
self.endpoint = endpoint
181176
self._use_notebook = use_notebook
182177
if use_notebook:
178+
import tqdm.notebook as tqdm_notebook
179+
183180
self.tqdm_bar = tqdm_notebook.tqdm
184181
self._connection = Connection(self.api_key, self.endpoint)
185182
self.validate = Validate(self.api_key, self.endpoint)

nucleus/errors.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
import pkg_resources
2-
3-
nucleus_client_version = pkg_resources.get_distribution(
4-
"scale-nucleus"
5-
).version
6-
71
INFRA_FLAKE_MESSAGES = [
82
"downstream duration timeout",
93
"upstream connect error or disconnect/reset before headers. reset reason: local reset",
@@ -40,6 +34,12 @@ class NucleusAPIError(Exception):
4034
def __init__(
4135
self, endpoint, command, requests_response=None, aiohttp_response=None
4236
):
37+
import pkg_resources
38+
39+
nucleus_client_version = pkg_resources.get_distribution(
40+
"scale-nucleus"
41+
).version
42+
4343
self.message = f"Your client is on version {nucleus_client_version}. If you have not recently done so, please make sure you have updated to the latest version of the client by running pip install --upgrade scale-nucleus\n"
4444
if requests_response is not None:
4545
self.status_code = requests_response.status_code

nucleus/metrics/cuboid_utils.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -99,15 +99,15 @@ def process_dataitem(dataitem):
9999

100100

101101
def compute_outer_iou(
102-
xyz_0: np.ndarray,
103-
wlh_0: np.ndarray,
104-
yaw_0: np.ndarray,
105-
xyz_1: np.ndarray,
106-
wlh_1: np.ndarray,
107-
yaw_1: np.ndarray,
102+
xyz_0: "np.ndarray",
103+
wlh_0: "np.ndarray",
104+
yaw_0: "np.ndarray",
105+
xyz_1: "np.ndarray",
106+
wlh_1: "np.ndarray",
107+
yaw_1: "np.ndarray",
108108
scale_convention: bool = True,
109109
distance_threshold=25,
110-
) -> Tuple[np.ndarray, np.ndarray]:
110+
) -> Tuple["np.ndarray", "np.ndarray"]:
111111
"""
112112
Computes outer 3D and 2D IoU
113113
:param xyz_0: (n, 3)
@@ -173,13 +173,13 @@ def compute_outer_iou(
173173

174174

175175
def get_batch_cuboid_corners(
176-
xyz: np.ndarray,
177-
wlh: np.ndarray,
178-
yaw: np.ndarray,
179-
pitch: np.ndarray = None,
180-
roll: np.ndarray = None,
176+
xyz: "np.ndarray",
177+
wlh: "np.ndarray",
178+
yaw: "np.ndarray",
179+
pitch: "np.ndarray" = None,
180+
roll: "np.ndarray" = None,
181181
scale_convention: bool = True,
182-
) -> np.ndarray:
182+
) -> "np.ndarray":
183183
"""
184184
Vectorized batch version of get_cuboid_corners
185185
:param xyz: (n, 3)
@@ -211,8 +211,8 @@ def get_batch_cuboid_corners(
211211

212212

213213
def get_batch_rotation_matrices(
214-
yaw: np.ndarray, pitch: np.ndarray = None, roll: np.ndarray = None
215-
) -> np.ndarray:
214+
yaw: "np.ndarray", pitch: "np.ndarray" = None, roll: "np.ndarray" = None
215+
) -> "np.ndarray":
216216
if pitch is None:
217217
pitch = np.zeros_like(yaw)
218218
if roll is None:
@@ -238,12 +238,12 @@ def get_batch_rotation_matrices(
238238

239239

240240
def associate_cuboids_on_iou(
241-
xyz_0: np.ndarray,
242-
wlh_0: np.ndarray,
243-
yaw_0: np.ndarray,
244-
xyz_1: np.ndarray,
245-
wlh_1: np.ndarray,
246-
yaw_1: np.ndarray,
241+
xyz_0: "np.ndarray",
242+
wlh_0: "np.ndarray",
243+
yaw_0: "np.ndarray",
244+
xyz_1: "np.ndarray",
245+
wlh_1: "np.ndarray",
246+
yaw_1: "np.ndarray",
247247
threshold_in_overlap_ratio: float = 0.1,
248248
) -> List[Tuple[int, int]]:
249249
if xyz_0.shape[0] < 1 or xyz_1.shape[0] < 1:
@@ -322,7 +322,7 @@ def detection_iou(
322322
prediction: List[CuboidPrediction],
323323
groundtruth: List[CuboidAnnotation],
324324
threshold_in_overlap_ratio: float,
325-
) -> Tuple[np.ndarray, np.ndarray]:
325+
) -> Tuple["np.ndarray", "np.ndarray"]:
326326
"""
327327
Calculates the 2D IOU and 3D IOU overlap between predictions and groundtruth.
328328
Uses linear sum assignment to associate cuboids.

0 commit comments

Comments
 (0)