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

Typehints2 #301

Closed
wants to merge 56 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
55eb6b4
add core mypy support to makefile/reqs
ankona Jun 1, 2023
858b77e
log.py typehints
ankona Jun 1, 2023
9a329a4
job.py typehints
ankona Jun 1, 2023
bebfab5
containers.py typehints
ankona Jun 1, 2023
fe61a8a
parser typehints
ankona Jun 1, 2023
3a03328
step class typehints
ankona Jun 1, 2023
504aec2
errors.py typehints
ankona Jun 1, 2023
47fc113
utils typehints
ankona Jun 1, 2023
b918dc1
manifest.py typehints
ankona Jun 1, 2023
d74a531
entity typehints 1
ankona Jun 1, 2023
171834b
controller.py typehints
ankona Jun 1, 2023
53f6221
redis.py typehints
ankona Jun 1, 2023
3569ac0
wlm settings typehints
ankona Jun 1, 2023
d18b8fc
generator.py typehints
ankona Jun 1, 2023
7fb8507
wlm init typehints
ankona Jun 1, 2023
0c96faf
_core.launcher utils typehints
ankona Jun 1, 2023
51a0267
core.utils helpers.py typehints
ankona Jun 1, 2023
f207b27
entitylist typehints
ankona Jun 1, 2023
1d7d0f2
experiment.py typehints
ankona Jun 1, 2023
468fc73
dbnode typehints
ankona Jun 1, 2023
a92a284
dbobject typehints
ankona Jun 2, 2023
95d24d3
model typehints
ankona Jun 2, 2023
e68d027
ensemble type hints
ankona Jun 2, 2023
c0f1953
orchestrator typehints
ankona Jun 2, 2023
a9fc067
typehint fixes
ankona Jun 3, 2023
48c6a8a
more typehint fixes
ankona Jun 3, 2023
eb9a3f9
ran black formatter, remove unused import
ankona Jun 3, 2023
822c7ba
launcher cmd typehint fix
ankona Jun 5, 2023
e7ac4f4
exe_args list of lists typehint fix
ankona Jun 5, 2023
fde3291
run settings input to string conversion typehint fixes
ankona Jun 5, 2023
f98e92b
add check-mypy CI action
ankona Jun 5, 2023
5e64017
build_batch_settings argument fixes for passing typecheck
ankona Jun 5, 2023
dd49604
use gh action matrix to parameterize python in type checking
ankona Jun 5, 2023
3d8fd80
combine mypy jobs with existing job to avoid matrix sync issues
ankona Jun 5, 2023
da83abf
docstring fix
ankona Jun 6, 2023
6889a18
combine mypy deps into regular dev reqs.txt
ankona Jun 6, 2023
4463331
remove python version from .toml, rely on default/installed python ver
ankona Jun 6, 2023
21ba60a
Alias StrategyFunction typehint
ankona Jun 6, 2023
deaa270
revert permutations refactor
ankona Jun 6, 2023
099aadb
Update action after combining reqs-mypy and reqs-dev
ankona Jun 6, 2023
6771fc6
update deps in setup.py, update changelog
ankona Jun 6, 2023
cb6e472
adjust mypy version to avoid dependency conflict
ankona Jun 6, 2023
049853a
avoid dep conflict by skipping mypy w/tf 2.6.2
ankona Jun 7, 2023
d857bdd
separate mypy deps & conditionally run check
ankona Jun 7, 2023
49b1dac
internal typehints batch 1
ankona Jun 6, 2023
508bea9
convert returncode to int type
ankona Jun 6, 2023
be0f3c8
omit/fix from batch 1
ankona Jun 6, 2023
2ce2bd7
typehints batch 2
ankona Jun 7, 2023
b57aaae
typehints batch 3
ankona Jun 7, 2023
48db139
local launcher typehints
ankona Jun 7, 2023
d2f6770
lsf/pbs/slurm launcher typehints
ankona Jun 7, 2023
4eb887f
error typehints
ankona Jun 7, 2023
404427d
typehints batch 4, generator & friends
ankona Jun 8, 2023
4f78527
typehints batch 5, control, entrypoints, generator, utils
ankona Jun 8, 2023
8d0644b
jobmanager typehints
ankona Jun 8, 2023
8c4bf47
job/jobmanager typehints 2
ankona Jun 8, 2023
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
7 changes: 7 additions & 0 deletions .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,10 @@ jobs:
with:
fail_ci_if_error: true
files: ./coverage.xml

- name: Run mypy
# TF 2.6.2 has a dep conflict with new mypy versions
if: (matrix.rai != '1.2.5')
run: |
python -m pip install .[mypy]
make check-mypy
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ check-lint:
@pylint --rcfile=.pylintrc ./smartsim


# help: check-mypy - run static type check
.PHONY: check-mypy
check-mypy:
@mypy --config-file=./pyproject.toml


# help:
# help: Documentation
# help: -------
Expand Down
3 changes: 3 additions & 0 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ former began deprecation in May 2022 and was finally removed in May 2023. (PR285
codes. These have now all been updated. (PR284_)
- Orchestrator and Colocated DB now accept a list of interfaces to bind to. The
argument name is still `interface` for backward compatibility reasons. (PR281_)
- Typehints have been added to public APIs. A makefile target to execute static
analysis with mypy is available `make check-mypy`. (PR295_)

.. _PR292: https://github.com/CrayLabs/SmartSim/pull/292
.. _PR291: https://github.com/CrayLabs/SmartSim/pull/291
Expand All @@ -57,6 +59,7 @@ argument name is still `interface` for backward compatibility reasons. (PR281_)
.. _PR285: https://github.com/CrayLabs/SmartSim/pull/285
.. _PR284: https://github.com/CrayLabs/SmartSim/pull/284
.. _PR281: https://github.com/CrayLabs/SmartSim/pull/281
.. _PR295: https://github.com/CrayLabs/SmartSim/pull/295

0.4.2
-----
Expand Down
37 changes: 37 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,40 @@ ignore_errors = true

[tool.coverage.html]
directory = "htmlcov"

[tool.mypy]
namespace_packages = true
files = [
"smartsim"
]
plugins = []
ignore_errors = true

[[tool.mypy.overrides]]
# Ignore packages that are not used or not typed
module = [
"coloredlogs",
"smartredis",
"smartredis.error"
]
ignore_missing_imports = true
ignore_errors = true

[[tool.mypy.overrides]]
module = [
"smartsim.database.*",
"smartsim.entity.*",
"smartsim.experiment"
]

ignore_errors=false

# Strict fn defs
disallow_untyped_calls = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
disallow_untyped_decorators = true

# Safety/Upgrading Mypy
warn_unused_ignores = true
# warn_redundant_casts = true # not a per-module setting?
11 changes: 11 additions & 0 deletions requirements-mypy.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
mypy>=1.3.0

# From typeshed
types-psutil
types-redis
types-tabulate
types-tqdm
types-tensorflow
types-setuptools

# Not from typeshed
9 changes: 9 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,15 @@ def has_ext_modules(_placeholder):
"pytest-cov>=2.10.1",
"click==8.0.2",
],
"mypy": [
"mypy>=1.3.0",
"types-psutil",
"types-redis",
"types-tabulate",
"types-tqdm",
"types-tensorflow",
"types-setuptools",
],
# see smartsim/_core/_install/buildenv.py for more details
"ml": versions.ml_extras_required(),
}
Expand Down
65 changes: 33 additions & 32 deletions smartsim/_core/_install/buildenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import site
import subprocess
import sys
import typing as t
from pathlib import Path
from typing import Iterable

Expand Down Expand Up @@ -67,7 +68,7 @@ class Version_(str):
includes some helper methods for comparing versions.
"""

def _convert_to_version(self, vers):
def _convert_to_version(self, vers: t.Union[str, Iterable, Version]) -> Version:
if isinstance(vers, Version):
return vers
elif isinstance(vers, str):
Expand All @@ -78,56 +79,56 @@ def _convert_to_version(self, vers):
raise InvalidVersion(vers)

@property
def major(self):
def major(self) -> int:
# Version(self).major doesn't work for all Python distributions
# see https://github.com/lebedov/python-pdfbox/issues/28
return int(pkg_resources.parse_version(self).base_version.split(".")[0])

@property
def minor(self):
def minor(self) -> int:
return int(pkg_resources.parse_version(self).base_version.split(".")[1])

@property
def micro(self):
def micro(self) -> int:
return int(pkg_resources.parse_version(self).base_version.split(".")[2])

@property
def patch(self):
def patch(self) -> str:
# return micro with string modifier i.e. 1.2.3+cpu -> 3+cpu
return str(pkg_resources.parse_version(self)).split(".")[2]

def __gt__(self, cmp):
def __gt__(self, cmp: object) -> bool:
try:
return Version(self).__gt__(self._convert_to_version(cmp))
except InvalidVersion:
return super().__gt__(cmp)

def __lt__(self, cmp):
def __lt__(self, cmp: object) -> bool:
try:
return Version(self).__lt__(self._convert_to_version(cmp))
except InvalidVersion:
return super().__lt__(cmp)

def __eq__(self, cmp):
def __eq__(self, cmp: object) -> bool:
try:
return Version(self).__eq__(self._convert_to_version(cmp))
except InvalidVersion:
return super().__eq__(cmp)

def __ge__(self, cmp):
def __ge__(self, cmp: object) -> bool:
try:
return Version(self).__ge__(self._convert_to_version(cmp))
except InvalidVersion:
return super().__ge__(cmp)

def __le__(self, cmp):
def __le__(self, cmp: object) -> bool:
try:
return Version(self).__le__(self._convert_to_version(cmp))
except InvalidVersion:
return super().__le__(cmp)


def get_env(var, default):
def get_env(var: str, default: str) -> str:
return os.environ.get(var, default)


Expand Down Expand Up @@ -182,7 +183,7 @@ class RedisAIVersion(Version_):
if sys.platform == "darwin":
defaults.pop("1.2.5", None)

def __init__(self, vers):
def __init__(self, vers: str) -> None:
min_rai_version = min(Version_(ver) for ver in self.defaults)
if min_rai_version > vers:
raise SetupError(
Expand All @@ -200,7 +201,7 @@ def __init__(self, vers):
else:
self.version = vers

def __getattr__(self, name):
def __getattr__(self, name: str) -> str:
try:
return self.defaults[self.version][name]
except KeyError:
Expand All @@ -213,7 +214,7 @@ def __getattr__(self, name):
"https://www.craylabs.org/docs/community.html"
) from None

def get_defaults(self):
def get_defaults(self) -> t.Dict[str, str]:
return self.defaults[self.version].copy()


Expand Down Expand Up @@ -277,7 +278,7 @@ class Versioner:
except AttributeError:
ONNX = None

def as_dict(self, db_name="REDIS"):
def as_dict(self, db_name: str = "REDIS") -> t.Dict[str, t.Any]:
packages = [
"SMARTSIM",
"SMARTREDIS",
Expand All @@ -300,7 +301,7 @@ def as_dict(self, db_name="REDIS"):
vers = {"Packages": packages, "Versions": versions}
return vers

def ml_extras_required(self):
def ml_extras_required(self) -> t.List[str]:
"""Optional ML/DL dependencies we suggest for the user.

The defaults are based on the RedisAI version
Expand All @@ -323,7 +324,7 @@ def ml_extras_required(self):

return [f"{lib}=={vers}" for lib, vers in ml_defaults.items()]

def get_sha(self, setup_py_dir) -> str:
def get_sha(self, setup_py_dir: str) -> str:
"""Get the git sha of the current branch"""
try:
sha = (
Expand All @@ -336,7 +337,7 @@ def get_sha(self, setup_py_dir) -> str:
# return empty string if not in git-repo
return ""

def write_version(self, setup_py_dir):
def write_version(self, setup_py_dir: str) -> str:
"""
Write version info to version.py

Expand Down Expand Up @@ -392,17 +393,17 @@ class BuildEnv:
CHECKS = int(os.environ.get("NO_CHECKS", 0))
PLATFORM = sys.platform

def __init__(self, checks=True):
def __init__(self, checks: bool = True) -> None:
if checks:
self.check_dependencies()

def check_dependencies(self):
def check_dependencies(self) -> None:
deps = ["git", "git-lfs", "make", "wget", "cmake", self.CC, self.CXX]
if int(self.CHECKS) == 0:
for dep in deps:
self.check_build_dependency(dep)

def __call__(self):
def __call__(self) -> t.Dict[str, str]:
# return the build env for the build process
env = os.environ.copy()
env.update(
Expand All @@ -415,7 +416,7 @@ def __call__(self):
)
return env

def as_dict(self):
def as_dict(self) -> t.Dict[str, str]:
variables = [
"CC",
"CXX",
Expand All @@ -440,27 +441,27 @@ def as_dict(self):
return env

@property
def python_version(self):
def python_version(self) -> str:
return platform.python_version()

def is_compatible_python(self, python_min):
def is_compatible_python(self, python_min: float) -> bool:
"""Detect if system Python is too old"""
sys_py = sys.version_info
system_python = Version_(f"{sys_py.major}.{sys_py.minor}.{sys_py.micro}")
return system_python > python_min

def is_windows(self):
def is_windows(self) -> bool:
return self.PLATFORM in ["win32", "cygwin", "msys"]

def is_macos(self):
def is_macos(self) -> bool:
return self.PLATFORM == "darwin"

@property
def torch_cmake_path(self):
def torch_cmake_path(self) -> t.Optional[str]:
"""Find the path to the cmake directory within a
pip installed pytorch package"""

def _torch_import_path():
def _torch_import_path() -> t.Optional[Path]:
"""Find through importing torch"""
try:
import torch as t
Expand All @@ -474,7 +475,7 @@ def _torch_import_path():
except ModuleNotFoundError:
return None

def _torch_site_path():
def _torch_site_path() -> t.Optional[Path]:
"""find torch through site packages"""
site_paths = [Path(p) for p in site.getsitepackages()]

Expand All @@ -496,7 +497,7 @@ def _torch_site_path():
return str(torch_path)

@staticmethod
def get_cudnn_env():
def get_cudnn_env() -> t.Dict[str, t.Optional[str]]:
"""Collect the environment variables needed for Caffe (Pytorch)
and throw an error if they are not found

Expand Down Expand Up @@ -529,7 +530,7 @@ def get_cudnn_env():
env["CUDNN_LIBRARY"] = env["CUDNN_LIBRARY_PATH"]
return env

def check_build_dependency(self, command):
def check_build_dependency(self, command: str) -> None:
# TODO expand this to parse and check versions.
try:
subprocess.check_call(
Expand All @@ -541,7 +542,7 @@ def check_build_dependency(self, command):
raise SetupError(f"{command} must be installed to build SmartSim") from None

@staticmethod
def check_installed(package, version=None):
def check_installed(package: str, version: t.Optional[Version_] = None) -> bool:
"""Check if a package is installed. If version is provided, check if
it's a compatible version. (major and minor the same)"""
try:
Expand Down
4 changes: 2 additions & 2 deletions smartsim/_core/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@


class Config:
def __init__(self):
def __init__(self) -> None:
# SmartSim/smartsim/_core
self.core_path = Path(os.path.abspath(__file__)).parent.parent

Expand Down Expand Up @@ -193,6 +193,6 @@ def test_account(self) -> str: # pragma: no cover


@lru_cache(maxsize=128, typed=False)
def get_config():
def get_config() -> Config:
# wrap into a function with a cached result
return Config()
Loading