Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more typing #304

Merged
merged 2 commits into from
Nov 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/action@v2.0.0
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