Skip to content

Commit 06fa9e0

Browse files
Use type hinting generics from standard collections
This eliminates all uses of these members of 'typing': Tuple, List, Dict, Set, FrozenSet, Type
1 parent 1549a96 commit 06fa9e0

File tree

6 files changed

+54
-54
lines changed

6 files changed

+54
-54
lines changed

scuba/config.py

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from pathlib import Path
55
import re
66
import shlex
7-
from typing import Any, List, Dict, Optional, TextIO, Tuple, Type, TypeVar, Union
7+
from typing import Any, Optional, TextIO, TypeVar, Union
88
from typing import overload
99

1010
import yaml
@@ -15,8 +15,8 @@
1515
from .dockerutil import make_vol_opt
1616

1717
CfgNode = Any
18-
CfgData = Dict[str, CfgNode]
19-
Environment = Dict[str, str]
18+
CfgData = dict[str, CfgNode]
19+
Environment = dict[str, str]
2020
_T = TypeVar("_T")
2121

2222
VOLUME_NAME_PATTERN = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9_.-]+$")
@@ -59,7 +59,7 @@ class OverrideStr(str, OverrideMixin):
5959
# http://stackoverflow.com/a/9577670
6060
class Loader(yaml.SafeLoader):
6161
_root: Path # directory containing the loaded document
62-
_cache: Dict[Path, Any] # document path => document
62+
_cache: dict[Path, Any] # document path => document
6363

6464
def __init__(self, stream: TextIO):
6565
if not hasattr(self, "_root"):
@@ -68,7 +68,7 @@ def __init__(self, stream: TextIO):
6868
super().__init__(stream)
6969

7070
@staticmethod
71-
def _rooted_loader(root: Path) -> Type[Loader]:
71+
def _rooted_loader(root: Path) -> type[Loader]:
7272
"""Get a Loader class with _root set to root"""
7373

7474
class RootedLoader(Loader):
@@ -160,7 +160,7 @@ def override(self, node: yaml.nodes.Node) -> OverrideMixin:
160160
Loader.add_constructor("!override", Loader.override)
161161

162162

163-
def find_config() -> Tuple[Path, Path, ScubaConfig]:
163+
def find_config() -> tuple[Path, Path, ScubaConfig]:
164164
"""Search up the directory hierarchy for .scuba.yml
165165
166166
Returns: path, rel, config on success, or None if not found
@@ -218,7 +218,7 @@ def _expand_env_vars(in_str: str) -> str:
218218
) from ve
219219

220220

221-
def _process_script_node(node: CfgNode, name: str) -> List[str]:
221+
def _process_script_node(node: CfgNode, name: str) -> list[str]:
222222
"""Process a script-type node
223223
224224
Args:
@@ -274,7 +274,7 @@ def _process_environment(node: CfgNode, name: str) -> Environment:
274274
return result
275275

276276

277-
def _get_nullable_str(data: Dict[str, Any], key: str) -> Optional[str]:
277+
def _get_nullable_str(data: dict[str, Any], key: str) -> Optional[str]:
278278
# N.B. We can't use data.get() here, because that might return
279279
# None, leading to ambiguity between the key being absent or set
280280
# to a null value.
@@ -303,7 +303,7 @@ def _get_entrypoint(data: CfgData) -> Optional[str]:
303303
return _get_nullable_str(data, "entrypoint")
304304

305305

306-
def _get_docker_args(data: CfgData) -> Optional[List[str]]:
306+
def _get_docker_args(data: CfgData) -> Optional[list[str]]:
307307
args_str = _get_nullable_str(data, "docker_args")
308308
if args_str is None:
309309
return None
@@ -319,7 +319,7 @@ def _get_docker_args(data: CfgData) -> Optional[List[str]]:
319319
def _get_typed_val(
320320
data: CfgData,
321321
key: str,
322-
type_: Type[_T],
322+
type_: type[_T],
323323
default: Optional[_T] = None,
324324
) -> Optional[_T]:
325325
v = data.get(key, default)
@@ -346,14 +346,14 @@ def _get_dict(data: CfgData, key: str) -> Optional[dict[str, Any]]:
346346
return _get_typed_val(data, key, dict)
347347

348348

349-
def _get_delimited_str_list(data: CfgData, key: str, sep: str) -> List[str]:
349+
def _get_delimited_str_list(data: CfgData, key: str, sep: str) -> list[str]:
350350
s = _get_typed_val(data, key, str)
351351
return s.split(sep) if s else []
352352

353353

354354
def _get_volumes(
355355
data: CfgData, scuba_root: Optional[Path]
356-
) -> Optional[Dict[Path, ScubaVolume]]:
356+
) -> Optional[dict[Path, ScubaVolume]]:
357357
voldata = _get_dict(data, "volumes")
358358
if voldata is None:
359359
return None
@@ -414,7 +414,7 @@ class ScubaVolume:
414414
container_path: Path
415415
host_path: Optional[Path] = None
416416
volume_name: Optional[str] = None
417-
options: List[str] = dataclasses.field(default_factory=list)
417+
options: list[str] = dataclasses.field(default_factory=list)
418418

419419
def __post_init__(self) -> None:
420420
if sum(bool(x) for x in (self.host_path, self.volume_name)) != 1:
@@ -498,14 +498,14 @@ def get_vol_opt(self) -> str:
498498
@dataclasses.dataclass(frozen=True)
499499
class ScubaAlias:
500500
name: str
501-
script: List[str]
501+
script: list[str]
502502
image: Optional[str] = None
503503
entrypoint: Optional[str] = None
504-
environment: Optional[Dict[str, str]] = None
504+
environment: Optional[dict[str, str]] = None
505505
shell: Optional[str] = None
506506
as_root: bool = False
507-
docker_args: Optional[List[str]] = None
508-
volumes: Optional[Dict[Path, ScubaVolume]] = None
507+
docker_args: Optional[list[str]] = None
508+
volumes: Optional[dict[Path, ScubaVolume]] = None
509509

510510
@classmethod
511511
def from_dict(
@@ -534,10 +534,10 @@ def from_dict(
534534
class ScubaConfig:
535535
shell: str
536536
entrypoint: Optional[str]
537-
docker_args: Optional[List[str]] # TODO: drop Optional?
538-
volumes: Optional[Dict[Path, ScubaVolume]] # TODO: drop Optional? Dict?
539-
aliases: Dict[str, ScubaAlias]
540-
hooks: Dict[str, List[str]]
537+
docker_args: Optional[list[str]] # TODO: drop Optional?
538+
volumes: Optional[dict[Path, ScubaVolume]] # TODO: drop Optional? dict?
539+
aliases: dict[str, ScubaAlias]
540+
hooks: dict[str, list[str]]
541541
environment: Environment
542542

543543
def __init__(
@@ -578,15 +578,15 @@ def __init__(
578578

579579
def _load_aliases(
580580
self, data: CfgData, scuba_root: Optional[Path]
581-
) -> Dict[str, ScubaAlias]:
581+
) -> dict[str, ScubaAlias]:
582582
aliases = {}
583583
for name, node in data.get("aliases", {}).items():
584584
if " " in name:
585585
raise ConfigError("Alias names cannot contain spaces")
586586
aliases[name] = ScubaAlias.from_dict(name, node, scuba_root)
587587
return aliases
588588

589-
def _load_hooks(self, data: CfgData) -> Dict[str, List[str]]:
589+
def _load_hooks(self, data: CfgData) -> dict[str, list[str]]:
590590
hooks = {}
591591
for name in (
592592
"user",

scuba/dockerutil.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22
import subprocess
33
import json
4-
from typing import Any, Dict, IO, Optional, Sequence, Union
4+
from typing import Any, IO, Optional, Sequence, Union
55
from pathlib import Path
66

77
# https://github.com/python/typeshed/blob/main/stdlib/subprocess.pyi
@@ -41,7 +41,7 @@ def call(
4141
def _run_docker(*args: str, capture: bool = False) -> subprocess.CompletedProcess[str]:
4242
"""Run docker and raise DockerExecuteError on ENOENT"""
4343
docker_args = ["docker"] + list(args)
44-
kw: Dict[str, Any] = dict(text=True)
44+
kw: dict[str, Any] = dict(text=True)
4545
if capture:
4646
kw.update(capture_output=True)
4747

@@ -89,7 +89,7 @@ def docker_inspect_or_pull(image: str) -> dict:
8989
def get_images() -> Sequence[str]:
9090
"""Get the current list of docker images
9191
92-
Returns: List of image names
92+
Returns: A list of image names
9393
"""
9494
cp = _run_docker(
9595
"images",

scuba/scuba.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from grp import getgrgid
1010
from pathlib import Path
1111
from pwd import getpwuid
12-
from typing import cast, Any, Dict, Iterable, List, Optional, Sequence, Tuple, Union
12+
from typing import cast, Any, Iterable, Optional, Sequence, Union
1313
from typing import TextIO
1414

1515
from .config import ScubaConfig, OverrideMixin
@@ -19,7 +19,7 @@
1919
from .dockerutil import make_vol_opt
2020
from .utils import shell_quote_cmd, flatten_list, get_umask, writeln
2121

22-
VolumeTuple = Tuple[Path, Path, List[str]]
22+
VolumeTuple = tuple[Path, Path, list[str]]
2323

2424

2525
class ScubaError(Exception):
@@ -28,21 +28,21 @@ class ScubaError(Exception):
2828

2929
class ScubaDive:
3030
context: ScubaContext
31-
env_vars: Dict[str, str]
32-
volumes: List[VolumeTuple]
33-
options: List[str]
34-
docker_args: List[str]
31+
env_vars: dict[str, str]
32+
volumes: list[VolumeTuple]
33+
options: list[str]
34+
docker_args: list[str]
3535

36-
docker_cmd: List[str]
36+
docker_cmd: list[str]
3737

3838
def __init__(
3939
self,
40-
user_command: List[str],
40+
user_command: list[str],
4141
config: ScubaConfig,
4242
top_path: Path,
4343
top_rel: Path,
44-
docker_args: Optional[List[str]] = None,
45-
env: Optional[Dict[str, str]] = None,
44+
docker_args: Optional[list[str]] = None,
45+
env: Optional[dict[str, str]] = None,
4646
as_root: bool = False,
4747
verbose: bool = False,
4848
image_override: Optional[str] = None,
@@ -143,7 +143,7 @@ def add_volume(
143143
self,
144144
hostpath: Union[Path, str],
145145
contpath: Union[Path, str],
146-
options: Optional[List[str]] = None,
146+
options: Optional[list[str]] = None,
147147
) -> None:
148148
"""Add a volume (bind-mount) to the docker run invocation"""
149149
hostpath = Path(hostpath)
@@ -375,11 +375,11 @@ def get_docker_cmdline(self) -> Sequence[str]:
375375
@dataclasses.dataclass
376376
class ScubaContext:
377377
image: str
378-
environment: Dict[str, str] # key: value
379-
volumes: Dict[Path, ScubaVolume]
378+
environment: dict[str, str] # key: value
379+
volumes: dict[Path, ScubaVolume]
380380
shell: str
381-
docker_args: List[str]
382-
script: Optional[List[str]] = None # TODO: drop Optional?
381+
docker_args: list[str]
382+
script: Optional[list[str]] = None # TODO: drop Optional?
383383
entrypoint: Optional[str] = None
384384
as_root: bool = False
385385

@@ -408,7 +408,7 @@ def process_command(
408408
entrypoint = cfg.entrypoint
409409
environment = copy.copy(cfg.environment)
410410
docker_args = copy.copy(cfg.docker_args) or []
411-
volumes: Dict[Path, ScubaVolume] = copy.copy(cfg.volumes or {})
411+
volumes: dict[Path, ScubaVolume] = copy.copy(cfg.volumes or {})
412412
as_root = False
413413

414414
if command:
@@ -429,7 +429,7 @@ def process_command(
429429
as_root = True
430430

431431
if isinstance(alias.docker_args, OverrideMixin) or docker_args is None:
432-
docker_args = cast(List[str], alias.docker_args)
432+
docker_args = cast(list[str], alias.docker_args)
433433
elif alias.docker_args is not None:
434434
docker_args.extend(alias.docker_args)
435435

tests/test_main.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import sys
1111
from tempfile import TemporaryFile, NamedTemporaryFile
1212
from textwrap import dedent
13-
from typing import cast, IO, List, Optional, Sequence, TextIO, Tuple
13+
from typing import cast, IO, Optional, Sequence, TextIO
1414
from unittest import mock
1515
import warnings
1616

@@ -27,7 +27,7 @@
2727
PseudoTTY,
2828
)
2929

30-
ScubaResult = Tuple[str, str]
30+
ScubaResult = tuple[str, str]
3131

3232

3333
SCUBA_YML = Path(".scuba.yml")
@@ -40,7 +40,7 @@ def write_script(path: Path, text: str) -> None:
4040

4141

4242
def run_scuba(
43-
args: List[str],
43+
args: list[str],
4444
*,
4545
expect_return: int = 0,
4646
mock_isatty: bool = False,
@@ -296,7 +296,7 @@ def _test_user(
296296
expected_username: str,
297297
expected_gid: int,
298298
expected_groupname: str,
299-
scuba_args: List[str] = [],
299+
scuba_args: list[str] = [],
300300
) -> None:
301301
SCUBA_YML.write_text(f"image: {DOCKER_IMAGE}")
302302

@@ -316,7 +316,7 @@ def _test_user(
316316
assert gid == expected_gid
317317
assert groupname == expected_groupname
318318

319-
def _test_user_expect_root(self, scuba_args: List[str] = []) -> None:
319+
def _test_user_expect_root(self, scuba_args: list[str] = []) -> None:
320320
return self._test_user(
321321
expected_uid=0,
322322
expected_username="root",
@@ -392,7 +392,7 @@ def test_user_root_alias(self) -> None:
392392

393393

394394
class TestMainHomedir(MainTest):
395-
def _test_home_writable(self, scuba_args: List[str] = []) -> None:
395+
def _test_home_writable(self, scuba_args: list[str] = []) -> None:
396396
SCUBA_YML.write_text(f"image: {DOCKER_IMAGE}")
397397

398398
args = scuba_args + [

tests/test_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
import os
44
import pytest
55
import shlex
6-
from typing import List, Sequence
6+
from typing import Sequence
77

88
from .utils import assert_seq_equal
99

1010
import scuba.utils
1111

1212

13-
def _parse_cmdline(cmdline: str) -> List[str]:
13+
def _parse_cmdline(cmdline: str) -> list[str]:
1414
# Strip the formatting and whitespace
1515
lines = [l.rstrip("\\").strip() for l in cmdline.splitlines()]
1616

tests/utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import unittest
88
import logging
99
from pathlib import Path
10-
from typing import Any, Callable, Dict, List, Sequence, TypeVar, Optional, Union
10+
from typing import Any, Callable, Sequence, TypeVar, Optional, Union
1111
from unittest import mock
1212

1313
from scuba.config import ScubaVolume
@@ -41,10 +41,10 @@ def assert_str_equalish(exp: Any, act: Any) -> None:
4141

4242

4343
def assert_vol(
44-
vols: Dict[Path, ScubaVolume],
44+
vols: dict[Path, ScubaVolume],
4545
cpath_str: PathStr,
4646
hpath_str: PathStr,
47-
options: List[str] = [],
47+
options: list[str] = [],
4848
) -> None:
4949
cpath = Path(cpath_str)
5050
hpath = Path(hpath_str)

0 commit comments

Comments
 (0)