Skip to content
Open
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
42 changes: 42 additions & 0 deletions .github/workflows/python-typecheck.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Python Type Check

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

on:
pull_request:
push:
branches:
- dev

jobs:
typecheck:
name: mypy --strict (baseline)
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- uses: actions/checkout@v3

- name: Setup Python
uses: actions/setup-python@v3
with:
python-version: '3.10'

- name: Cache pip
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: typecheck-${{ runner.os }}-${{ hashFiles('requirements-dev.txt') }}
restore-keys: typecheck-${{ runner.os }}-

- name: Install type-check dependencies
run: pip install -r requirements-dev.txt

- name: Run mypy with baseline filter
# Fails if any *new* violations are introduced beyond the frozen baseline.
# To update the baseline after fixing violations, run locally:
# python -m mypy -p aaz_dev | python -m mypy_baseline sync
# and commit the updated mypy-baseline.txt.
run: python -m mypy -p aaz_dev | python -m mypy_baseline filter
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,6 @@ src/aaz_dev/ui

.env
*/.env

# MonkeyType trace database
monkeytype.sqlite3
42 changes: 42 additions & 0 deletions monkeytype_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""
MonkeyType configuration for aaz-dev-tools.

Usage — trace, then apply:
PYTHONPATH=src/aaz_dev:src python -m monkeytype run -m pytest \
src/aaz_dev/command/tests/configuration_tests/ \
src/aaz_dev/command/tests/editor_tests/test_serialize_shorthand.py \
--ignore=src/aaz_dev/command/tests/configuration_tests/test_xml.py \
-q

python -m monkeytype list-modules
python -m monkeytype apply <module>

Notes:
- The code_filter restricts tracing to src/aaz_dev/ only, keeping Flask/schematics
stdlib internals out of the trace database.
- Module names in the DB use the unqualified form (e.g. "utils.case") because the
codebase adds src/aaz_dev/ to sys.path. Apply with those unqualified names.
"""

import os
from monkeytype.config import DefaultConfig
from monkeytype.typing import NoOpRewriter


_REPO_ROOT = os.path.dirname(os.path.abspath(__file__))
_SOURCE_ROOT = os.path.join(_REPO_ROOT, "src", "aaz_dev")


class AazDevConfig(DefaultConfig):
def code_filter(self): # type: ignore[override]
"""Only trace code that lives inside src/aaz_dev/."""
source_root = _SOURCE_ROOT

def _filter(code): # type: ignore[no-untyped-def]
filename = code.co_filename or ""
return filename.startswith(source_root)

return _filter


CONFIG = AazDevConfig()
5,483 changes: 5,483 additions & 0 deletions mypy-baseline.txt

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[tool.mypy]
python_version = "3.10"
strict = true
# Source layout: package lives under src/
mypy_path = "src"

# Third-party libraries without stubs — ignore missing imports for now
# rather than failing on every untyped dependency
ignore_missing_imports = true
5 changes: 5 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Dev dependencies — not required at runtime
mypy>=1.10.0
mypy-baseline>=0.6.0
monkeytype>=23.3.0
libcst>=1.0.0
3 changes: 2 additions & 1 deletion src/aaz_dev/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
from utils import exceptions
from utils.config import Config
from aaz_dev.app.run import run_command
import flask.app


def create_app():
def create_app() -> flask.app.Flask:
app = Flask(__name__, static_folder=Config.STATIC_FOLDER, static_url_path=Config.STATIC_URL_PATH)
logger = create_logger(app)

Expand Down
4 changes: 3 additions & 1 deletion src/aaz_dev/cli/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@

def register_blueprints(app):
from flask.app import Flask

def register_blueprints(app: Flask) -> None:
from . import az, portal, _cmds
app.register_blueprint(_cmds.bp)
app.register_blueprint(az.bp)
Expand Down
9 changes: 5 additions & 4 deletions src/aaz_dev/command/controller/command_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from command.model.specs._command_tree import CMDSpecsSimpleCommand, CMDSpecsSimpleCommandGroup, \
CMDSpecsSimpleCommandTree
from utils import exceptions
from typing import List, Any

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -59,7 +60,7 @@ def build_simple_command_tree(aaz_path):


class CMDSpecsPartialCommandGroup:
def __init__(self, names, short_help, uri, aaz_path):
def __init__(self, names: List[str], short_help: str, uri: str, aaz_path: str):
self.names = names
self.short_help = short_help
self.uri = uri
Expand Down Expand Up @@ -316,12 +317,12 @@ def parse_command_info(cls, info, cmd_names):


class CMDSpecsPartialCommandTree:
def __init__(self, aaz_path, root=None):
def __init__(self, aaz_path: str, root: None=None):
self.aaz_path = aaz_path
self._root = root or CMDSpecsPartialCommandGroup(names=["aaz"], short_help='', uri="/Commands/readme.md",
aaz_path=aaz_path).load()
self._modified_command_groups = set()
self._modified_commands = set()
self._modified_command_groups: set[Any] = set()
self._modified_commands: set[Any] = set()

@property
def root(self):
Expand Down
4 changes: 3 additions & 1 deletion src/aaz_dev/command/controller/shorthand.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
def serialize(obj):
from typing import Any

def serialize(obj: Any) -> str:
def dfs(obj):
if isinstance(obj, dict):
return "{" + ",".join(f'{k}:{dfs(v)}' for k, v in obj.items()) + "}"
Expand Down
1 change: 1 addition & 0 deletions src/aaz_dev/command/controller/workspace_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from .specs_manager import AAZSpecsManager
from .workspace_cfg_editor import WorkspaceCfgEditor, build_endpoint_selector_for_client_config
from .workspace_client_cfg_editor import WorkspaceClientCfgEditor
from typing import Any, Optional

logger = logging.getLogger('aaz')

Expand Down
21 changes: 12 additions & 9 deletions src/aaz_dev/command/model/configuration/_arg.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
from utils import exceptions

import copy
from typing import TYPE_CHECKING, Any, Type, Union
if TYPE_CHECKING:
from command.model.configuration._arg_builder import CMDArgBuilder


class CMDArgEnumItem(Model):
Expand Down Expand Up @@ -129,12 +132,12 @@ class Options:
def type(self):
return self._get_type()

def _get_type(self):
def _get_type(self) -> str:
assert self.TYPE_VALUE is not None
return self.TYPE_VALUE

@classmethod
def _claim_polymorphic(cls, data):
def _claim_polymorphic(cls, data: Any) -> bool:
if cls.TYPE_VALUE is None:
return False

Expand All @@ -148,7 +151,7 @@ def _claim_polymorphic(cls, data):
return False

@classmethod
def build_arg_base(cls, builder):
def build_arg_base(cls, builder: "CMDArgBuilder") -> Any:
arg_base = cls()
arg_base.nullable = builder.get_nullable()
arg_base.blank = builder.get_blank()
Expand All @@ -171,7 +174,7 @@ def __init__(self, **kwargs):
**kwargs
)

def find_model(self, data):
def find_model(self, data: Any) -> Any:
if self.claim_function:
kls = self.claim_function(self, data)
if not kls:
Expand Down Expand Up @@ -229,7 +232,7 @@ def __init__(self, *args, **kwargs):
self.ref_schema = None

@classmethod
def _claim_polymorphic(cls, data):
def _claim_polymorphic(cls, data: Any) -> bool:
if super()._claim_polymorphic(data):
if isinstance(data, dict):
# distinguish with CMDArgBase and CMDArg
Expand All @@ -239,7 +242,7 @@ def _claim_polymorphic(cls, data):
return False

@classmethod
def build_arg(cls, builder):
def build_arg(cls, builder: "CMDArgBuilder") -> Any:
arg = cls.build_arg_base(builder)
assert isinstance(arg, CMDArg)
arg.var = builder.get_var()
Expand Down Expand Up @@ -278,7 +281,7 @@ def __init__(self, *args, **kwargs):
self.implement = None

@classmethod
def _claim_polymorphic(cls, data):
def _claim_polymorphic(cls, data: Any) -> bool:
if isinstance(data, dict):
type_value = data.get('type', None)
if type_value is not None and type_value.startswith("@"):
Expand Down Expand Up @@ -356,7 +359,7 @@ class CMDStringArgBase(CMDArgBase):
enum = ModelType(CMDArgEnum)

@classmethod
def build_arg_base(cls, builder):
def build_arg_base(cls, builder: "CMDArgBuilder") -> Any:
arg = super().build_arg_base(builder)
assert isinstance(arg, CMDStringArgBase)
arg.fmt = builder.get_fmt()
Expand Down Expand Up @@ -838,7 +841,7 @@ class CMDArrayArgBase(CMDArgBase):
# default
cls = CMDClassField()

def _get_type(self):
def _get_type(self) -> str:
return f"{self.TYPE_VALUE}<{self.item.type}>"

@classmethod
Expand Down
Loading
Loading