Skip to content

Commit

Permalink
Add more typing (#304)
Browse files Browse the repository at this point in the history
  • Loading branch information
blink1073 authored Nov 8, 2022
1 parent 17727a3 commit d368e1f
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 75 deletions.
18 changes: 3 additions & 15 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
pip install -e ".[test]"
- name: Test with pytest
run: |
python -m pytest -vv --timeout 60 --cov jupyter_core --cov-report term-missing:skip-covered
pytest -vv --timeout 60 --cov jupyter_core --cov-report term-missing:skip-covered
- name: Check CLI
run: |
cd $HOME
Expand Down Expand Up @@ -103,17 +103,5 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: pre-commit/[email protected]
with:
extra_args: --all-files --hook-stage=manual
- name: Help message if pre-commit fail
if: ${{ failure() }}
run: |
echo "You can install pre-commit hooks to automatically run formatting"
echo "on each commit with:"
echo " pre-commit install"
echo "or you can run by hand on staged files with"
echo " pre-commit run"
echo "or after-the-fact on already committed files with"
echo " pre-commit run --all-files --hook-stage=manual"
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
- uses: jupyterlab/maintainer-tools/.github/actions/pre-commit@v1
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ repos:
- id: mypy
args: ["--config-file", "pyproject.toml"]
stages: [manual]
additional_dependencies: [pytest, platformdirs]
additional_dependencies: [pytest, platformdirs, traitlets, ipykernel]
exclude: |
exclude: |
(?x)^(
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@

# -- Options for LaTeX output ---------------------------------------------

latex_elements: dict = {}
# latex_elements: dict = {}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
Expand Down
23 changes: 15 additions & 8 deletions jupyter_core/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import logging
import os
import sys
import typing as t
from copy import deepcopy
from shutil import which

Expand Down Expand Up @@ -74,12 +75,12 @@ class JupyterApp(Application):
def _log_level_default(self):
return logging.INFO

jupyter_path = List(Unicode())
jupyter_path: t.Union[t.List[str], List] = List(Unicode())

def _jupyter_path_default(self):
return jupyter_path()

config_dir = Unicode()
config_dir: t.Union[str, Unicode] = Unicode()

def _config_dir_default(self):
return jupyter_config_dir()
Expand All @@ -92,14 +93,14 @@ def config_file_paths(self):
path.insert(0, self.config_dir)
return path

data_dir = Unicode()
data_dir: t.Union[str, Unicode] = Unicode()

def _data_dir_default(self):
d = jupyter_data_dir()
ensure_dir_exists(d, mode=0o700)
return d

runtime_dir = Unicode()
runtime_dir: t.Union[str, Unicode] = Unicode()

def _runtime_dir_default(self):
rd = jupyter_runtime_dir()
Expand All @@ -110,21 +111,27 @@ def _runtime_dir_default(self):
def _runtime_dir_changed(self, change):
ensure_dir_exists(change["new"], mode=0o700)

generate_config = Bool(False, config=True, help="""Generate default config file.""")
generate_config: t.Union[bool, Bool] = Bool(
False, config=True, help="""Generate default config file."""
)

config_file_name = Unicode(config=True, help="Specify a config file to load.")
config_file_name: t.Union[str, Unicode] = Unicode(
config=True, help="Specify a config file to load."
)

def _config_file_name_default(self):
if not self.name:
return ""
return self.name.replace("-", "_") + "_config"

config_file = Unicode(
config_file: t.Union[str, Unicode] = Unicode(
config=True,
help="""Full path of a config file.""",
)

answer_yes = Bool(False, config=True, help="""Answer yes to any prompts.""")
answer_yes: t.Union[bool, Bool] = Bool(
False, config=True, help="""Answer yes to any prompts."""
)

def write_default_config(self):
"""Write our default config to a .py config file"""
Expand Down
7 changes: 4 additions & 3 deletions jupyter_core/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import sysconfig
from shutil import which
from subprocess import Popen
from typing import List

from . import paths
from .version import __version__
Expand All @@ -35,7 +36,7 @@ def epilog(self, x):
pass


def jupyter_parser():
def jupyter_parser() -> JupyterParser:
parser = JupyterParser(
description="Jupyter: Interactive Computing",
)
Expand All @@ -60,7 +61,7 @@ def jupyter_parser():
return parser


def list_subcommands():
def list_subcommands() -> List[str]:
"""List all jupyter subcommands
searches PATH for `jupyter-name`
Expand Down Expand Up @@ -169,7 +170,7 @@ def _path_with_self():
return path_list


def main():
def main() -> None:
parser = jupyter_parser()
if len(sys.argv) > 1 and not sys.argv[1].startswith("-"):
# Don't parse if a subcommand is given
Expand Down
2 changes: 1 addition & 1 deletion jupyter_core/migrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import shutil
from datetime import datetime

from traitlets.config import JSONFileConfigLoader, PyFileConfigLoader
from traitlets.config.loader import JSONFileConfigLoader, PyFileConfigLoader
from traitlets.log import get_logger

from .application import JupyterApp
Expand Down
58 changes: 29 additions & 29 deletions jupyter_core/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import warnings
from contextlib import contextmanager
from pathlib import Path
from typing import Optional
from typing import Any, Dict, Iterator, List, Optional

import platformdirs

Expand All @@ -33,7 +33,7 @@
UF_HIDDEN = getattr(stat, "UF_HIDDEN", 32768)


def envset(name, default=False):
def envset(name: str, default: Optional[bool] = False) -> Optional[bool]:
"""Return the boolean value of a given environment variable.
An environment variable is considered set if it is assigned to a value
Expand All @@ -47,16 +47,16 @@ def envset(name, default=False):
return os.environ[name].lower() not in ["no", "n", "false", "off", "0", "0.0"]


def use_platform_dirs():
def use_platform_dirs() -> bool:
"""Determine if platformdirs should be used for system-specific paths.
We plan for this to default to False in jupyter_core version 5 and to True
in jupyter_core version 6.
"""
return envset("JUPYTER_PLATFORM_DIRS", False)
return envset("JUPYTER_PLATFORM_DIRS", False) # type:ignore[return-value]


def get_home_dir():
def get_home_dir() -> str:
"""Get the real path of the home directory"""
homedir = os.path.expanduser("~")
# Next line will make things work even when /home/ is a symlink to
Expand All @@ -65,14 +65,14 @@ def get_home_dir():
return homedir


_dtemps: dict = {}
_dtemps: Dict[str, str] = {}


def prefer_environment_over_user():
def prefer_environment_over_user() -> bool:
"""Determine if environment-level paths should take precedence over user-level paths."""
# If JUPYTER_PREFER_ENV_PATH is defined, that signals user intent, so return its value
if "JUPYTER_PREFER_ENV_PATH" in os.environ:
return envset("JUPYTER_PREFER_ENV_PATH")
return envset("JUPYTER_PREFER_ENV_PATH") # type:ignore[return-value]

# If we are in a Python virtualenv, default to True (see https://docs.python.org/3/library/venv.html#venv-def)
if sys.prefix != sys.base_prefix:
Expand All @@ -85,7 +85,7 @@ def prefer_environment_over_user():
return False


def _mkdtemp_once(name):
def _mkdtemp_once(name: str) -> str:
"""Make or reuse a temporary directory.
If this is called with the same name in the same process, it will return
Expand All @@ -98,7 +98,7 @@ def _mkdtemp_once(name):
return d


def jupyter_config_dir():
def jupyter_config_dir() -> str:
"""Get the Jupyter config directory for this platform and user.
Returns JUPYTER_CONFIG_DIR if defined, otherwise the appropriate
Expand All @@ -119,7 +119,7 @@ def jupyter_config_dir():
return pjoin(home_dir, ".jupyter")


def jupyter_data_dir():
def jupyter_data_dir() -> str:
"""Get the config directory for Jupyter data files for this platform and user.
These are non-transient, non-configuration files.
Expand Down Expand Up @@ -152,7 +152,7 @@ def jupyter_data_dir():
return pjoin(xdg, "jupyter")


def jupyter_runtime_dir():
def jupyter_runtime_dir() -> str:
"""Return the runtime dir for transient jupyter files.
Returns JUPYTER_RUNTIME_DIR if defined.
Expand Down Expand Up @@ -192,10 +192,10 @@ def jupyter_runtime_dir():
"/usr/share/jupyter",
]

ENV_JUPYTER_PATH = [os.path.join(sys.prefix, "share", "jupyter")]
ENV_JUPYTER_PATH: List[str] = [os.path.join(sys.prefix, "share", "jupyter")]


def jupyter_path(*subdirs):
def jupyter_path(*subdirs: str) -> List[str]:
"""Return a list of directories to search for data files
JUPYTER_PATH environment variable has highest priority.
Expand All @@ -217,7 +217,7 @@ def jupyter_path(*subdirs):
['~/.local/jupyter/kernels', '/usr/local/share/jupyter/kernels']
"""

paths: list = []
paths: List[str] = []

# highest priority is explicit environment variable
if os.environ.get("JUPYTER_PATH"):
Expand Down Expand Up @@ -273,10 +273,10 @@ def jupyter_path(*subdirs):
"/usr/local/etc/jupyter",
"/etc/jupyter",
]
ENV_CONFIG_PATH = [os.path.join(sys.prefix, "etc", "jupyter")]
ENV_CONFIG_PATH: List[str] = [os.path.join(sys.prefix, "etc", "jupyter")]


def jupyter_config_path():
def jupyter_config_path() -> List[str]:
"""Return the search path for Jupyter config files as a list.
If the JUPYTER_PREFER_ENV_PATH environment variable is set, the
Expand All @@ -290,7 +290,7 @@ def jupyter_config_path():
# jupyter_config_dir makes a blank config when JUPYTER_NO_CONFIG is set.
return [jupyter_config_dir()]

paths: list = []
paths: List[str] = []

# highest priority is explicit environment variable
if os.environ.get("JUPYTER_CONFIG_PATH"):
Expand Down Expand Up @@ -326,7 +326,7 @@ def jupyter_config_path():
return paths


def exists(path):
def exists(path: str) -> bool:
"""Replacement for `os.path.exists` which works for host mapped volumes
on Windows containers
"""
Expand All @@ -337,7 +337,7 @@ def exists(path):
return True


def is_file_hidden_win(abs_path, stat_res=None):
def is_file_hidden_win(abs_path: str, stat_res: Optional[Any] = None) -> bool:
"""Is a file hidden?
This only checks the file itself; it should be called in combination with
Expand Down Expand Up @@ -365,7 +365,7 @@ def is_file_hidden_win(abs_path, stat_res=None):
raise

try:
if stat_res.st_file_attributes & stat.FILE_ATTRIBUTE_HIDDEN: # type:ignore[attr-defined]
if stat_res.st_file_attributes & stat.FILE_ATTRIBUTE_HIDDEN: # type:ignore
return True
except AttributeError:
# allow AttributeError on PyPy for Windows
Expand All @@ -379,7 +379,7 @@ def is_file_hidden_win(abs_path, stat_res=None):
return False


def is_file_hidden_posix(abs_path, stat_res=None):
def is_file_hidden_posix(abs_path: str, stat_res: Optional[Any] = None) -> bool:
"""Is a file hidden?
This only checks the file itself; it should be called in combination with
Expand Down Expand Up @@ -407,7 +407,7 @@ def is_file_hidden_posix(abs_path, stat_res=None):
raise

# check that dirs can be listed
if stat.S_ISDIR(stat_res.st_mode):
if stat.S_ISDIR(stat_res.st_mode): # type:ignore[misc]
# use x-access, not actual listing, in case of slow/large listings
if not os.access(abs_path, os.X_OK | os.R_OK):
return True
Expand All @@ -425,7 +425,7 @@ def is_file_hidden_posix(abs_path, stat_res=None):
is_file_hidden = is_file_hidden_posix


def is_hidden(abs_path, abs_root=""):
def is_hidden(abs_path: str, abs_root: str = "") -> bool:
"""Is a file hidden or contained in a hidden directory?
This will start with the rightmost path element and work backwards to the
Expand Down Expand Up @@ -479,7 +479,7 @@ def is_hidden(abs_path, abs_root=""):
return False


def win32_restrict_file_to_user(fname):
def win32_restrict_file_to_user(fname: str) -> None:
"""Secure a windows file to read-only access for the user.
Follows guidance from win32 library creator:
http://timgolden.me.uk/python/win32_how_do_i/add-security-to-a-file.html
Expand Down Expand Up @@ -522,7 +522,7 @@ def win32_restrict_file_to_user(fname):
win32security.SetFileSecurity(fname, win32security.DACL_SECURITY_INFORMATION, sd)


def _win32_restrict_file_to_user_ctypes(fname):
def _win32_restrict_file_to_user_ctypes(fname: str) -> None:
"""Secure a windows file to read-only access for the user.
Follows guidance from win32 library creator:
Expand Down Expand Up @@ -896,7 +896,7 @@ def NewAcl():
SetFileSecurity(fname, DACL_SECURITY_INFORMATION, SelfRelativeSD)


def get_file_mode(fname):
def get_file_mode(fname: str) -> int:
"""Retrieves the file mode corresponding to fname in a filesystem-tolerant manner.
Parameters
Expand All @@ -919,7 +919,7 @@ def get_file_mode(fname):


@contextmanager
def secure_write(fname, binary=False):
def secure_write(fname: str, binary: bool = False) -> Iterator[Any]:
"""Opens a file in the most restricted pattern available for
writing content. This limits the file mode to `0o0600` and yields
the resulting opened filed handle.
Expand Down Expand Up @@ -973,7 +973,7 @@ def secure_write(fname, binary=False):
yield f


def issue_insecure_write_warning():
def issue_insecure_write_warning() -> None:
def format_warning(msg, *args, **kwargs):
return str(msg) + "\n"

Expand Down
Loading

0 comments on commit d368e1f

Please sign in to comment.