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

refactor: Add ruff rules, improve type annotations, improve ci performance #3431

Merged
merged 101 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
101 commits
Select commit Hold shift + click to select a range
de50aa9
ci: Add additional `ruff` rules to `pyproject.toml`
dangotbanned Jun 5, 2024
e019f17
fix: add item to `pyproject.toml` to silence import errors for `pylan…
dangotbanned Jun 5, 2024
54815ce
ci: add subset of `SIM` to `extend-safe-fixes`
dangotbanned Jun 5, 2024
1effcb1
ci: add unfixable `RUF` rules to `ignore`
dangotbanned Jun 5, 2024
a3fd77a
refactor: apply new `ruff` rules, fix and reformat
dangotbanned Jun 5, 2024
b6190f2
test: Skip tests on Win that require a tz database
dangotbanned Jun 5, 2024
0c57781
ci: enable `tool.ruff.lint.preview`
dangotbanned Jun 5, 2024
3171a3d
fix: replace [F841](https://docs.astral.sh/ruff/rules/unused-variable…
dangotbanned Jun 5, 2024
5ba210d
ci: add additional `preview` fixes to `extend-safe-fixes`
dangotbanned Jun 5, 2024
8ef6184
refactor: apply `preview` fixes for existing `ruff` rules, reformat
dangotbanned Jun 5, 2024
a16b292
ci: add `preview` category `FURB` rules
dangotbanned Jun 5, 2024
7fff541
refactor: apply `FURB` rule fixes, manually fix `FURB101/3`
dangotbanned Jun 5, 2024
217208b
fix: Revert newer fstring syntax, not available to `sphinx`
dangotbanned Jun 5, 2024
1e17533
ci: add fixable `pylint` rules
dangotbanned Jun 5, 2024
007b8b3
ci: add `pylint` fixes to `extend-safe-fixes`
dangotbanned Jun 5, 2024
10eb5d5
refactor: apply `pylint` rule fixes, add an inline optimization for `…
dangotbanned Jun 5, 2024
e1ab766
fix: Recover comments lost during linting
dangotbanned Jun 5, 2024
f3f6196
fix: Replace sources of `RUF002` violations
dangotbanned Jun 5, 2024
d4a554b
fix: manual fix `RUF002` in `api`
dangotbanned Jun 5, 2024
d584e08
ci: add `PTH` rules
dangotbanned Jun 6, 2024
ed2528b
refactor: Manually fix `PTH` rule violations
dangotbanned Jun 6, 2024
8393a5a
fix: Use safer `inspect.getattr_static` in `update_init_file`
dangotbanned Jun 6, 2024
38a3280
fix: Use correct f-string `!s` modifier
dangotbanned Jun 6, 2024
c7485dc
fix: Resolve `doc:build-html` error
dangotbanned Jun 6, 2024
626bc20
fix: Resolve utf-8 error for [emoji example](https://altair-viz.githu…
dangotbanned Jun 6, 2024
8e6a523
Update sphinxext/altairgallery.py
dangotbanned Jun 6, 2024
e935ad9
build: bump `docbuild.yml` python `3.10` -> `3.12`
dangotbanned Jun 7, 2024
9073a1f
style: Simplify `PluginRegistry` repr
dangotbanned Jun 7, 2024
de01aa5
revert: ignore `suppressible-exception` rule in `pyproject.toml`
dangotbanned Jun 7, 2024
80a7a22
refactor: remove need for exception handling in `utils._vegafusion_da…
dangotbanned Jun 7, 2024
b96c4e9
refactor: remove `python<3.3` compat code in `utils.core.use_signature`
dangotbanned Jun 7, 2024
88ec5c8
revert: replace `PLW1514` fixes with "utf-8"
dangotbanned Jun 7, 2024
59893bd
refactor: minor simplify and sort `__all__` from `generate_schema_wra…
dangotbanned Jun 7, 2024
03b4d09
docs: `FA100` on most of `tools/`*`
dangotbanned Jun 7, 2024
e17382a
docs: `FA100` on `sphinxext/*`
dangotbanned Jun 7, 2024
6f02e2e
docs: `FA100` on `alt.jupyter`
dangotbanned Jun 7, 2024
12a9bea
docs: `FA100` on `alt.expr`
dangotbanned Jun 7, 2024
c378b65
docs: `FA100` on `alt.vegalite`, excluding `v5.api`, `v5.schema/*`
dangotbanned Jun 8, 2024
f6da84e
docs: `FA100` on private `alt.utils` modules
dangotbanned Jun 8, 2024
185f2fd
docs(typing): Add annotations for `sphinxext`
dangotbanned Jun 8, 2024
e52e414
docs: `FA100` on public `alt.utils` modules, excluding `schemapi`
dangotbanned Jun 8, 2024
811d566
ci: update `pyproject.toml`, add new granular `tool.ruff.lint.per-fil…
dangotbanned Jun 8, 2024
dba02c9
revert: Change `write_file_or_filename` default `encoding` to `None`
dangotbanned Jun 8, 2024
a8597cb
docs: `FA100` on `tools.schemapi.schemapi`
dangotbanned Jun 8, 2024
6cfc769
feat(typing): add `from __future__ import annotations` for each file …
dangotbanned Jun 8, 2024
2a06b2e
ci(typing): adds `generate-schema-wrapper` script to `hatch`
dangotbanned Jun 9, 2024
337d844
refactor(typing): Improve annotations and narrowing in `schemapi`
dangotbanned Jun 9, 2024
76bc00b
build: run `generate-schema-wrapper`
dangotbanned Jun 9, 2024
ece2e85
revert(typing): Roll back runtime evaluated types not possible on old…
dangotbanned Jun 10, 2024
62f5eeb
fix(typing): Adds overloads and reduce ignore comments relating to `s…
dangotbanned Jun 10, 2024
eb5b30d
revert(typing): Roll back additional runtime evaluated types for `pyt…
dangotbanned Jun 10, 2024
d8a36e6
fix(typing): Use `...` for `DataTransformerType`
dangotbanned Jun 10, 2024
ea5a3d4
docs(typing): `FA100` on `alt.vegalite.v5.api` and manual annotation …
dangotbanned Jun 10, 2024
70e8e95
test: add pytest rules `PT` and fix `PT001` violations
dangotbanned Jun 10, 2024
8bbb78e
test: add ignores for `PT011` on existing tests
dangotbanned Jun 10, 2024
cd56353
test: fix `PT018` violation
dangotbanned Jun 10, 2024
27aab26
test: fix `PT006` violations
dangotbanned Jun 10, 2024
a820ed1
test: add ignore for `PT012` violation
dangotbanned Jun 10, 2024
4b38b87
test: add `pytest.mark.xfail` for flaky `scatter_with_layered_histogr…
dangotbanned Jun 10, 2024
25ce09b
ci: tidy up `ruff` section of `pyproject.toml`
dangotbanned Jun 10, 2024
fd20f00
perf: adds config `pyproject.toml` for faster build, test runs
dangotbanned Jun 10, 2024
ad9e70c
ci: adds `update-init-file` hatch script
dangotbanned Jun 10, 2024
8308b6e
ci: adds newer-style `hatch` test config
dangotbanned Jun 10, 2024
36da1c7
feat(typing): Use `TYPE_CHECKING` block for `schema` modules
dangotbanned Jun 10, 2024
538b527
test: use a more reliable `xfail` condition for flaky test
dangotbanned Jun 11, 2024
9582971
build: Embed extra `ruff` calls in `generate-schema-wrapper` into the…
dangotbanned Jun 11, 2024
33056a2
build: Manually rewrite certain exceptions with flaky autofix
dangotbanned Jun 11, 2024
5244100
refactor: remove now-unneeded `PARAMETER_PROTOCOL`
dangotbanned Jun 11, 2024
4dba0a6
refactor: replace existing references to `typing.Optional`
dangotbanned Jun 11, 2024
03c217c
refactor(typing): rename `T` TypeVar to `TSchemaBase`
dangotbanned Jun 11, 2024
fbc02e2
feat(typing): Adds dedicated `Optional` alias for `Union[..., Undefin…
dangotbanned Jun 11, 2024
f8aa86f
refactor(typing): Remove `UndefinedType` dependency in `api`
dangotbanned Jun 12, 2024
046faa8
refactor(typing): Remove `UndefinedType` dependency in `api`
dangotbanned Jun 12, 2024
05ae13f
refactor(typing): Remove annotation scope `UndefinedType` dependency …
dangotbanned Jun 12, 2024
30ff371
ci: add `W291` to ensure trailing whitespace is autofixed
dangotbanned Jun 12, 2024
dc98ae1
refactor: define non-relevant attributes closer to imports in `update…
dangotbanned Jun 12, 2024
f82adbd
feat(typing): Adds `_TypeAliasTracer` and reorders `generate_schema_w…
dangotbanned Jun 12, 2024
a7b0ab5
build: run `generate-schema-wrapper` using `_TypeAliasTracer`
dangotbanned Jun 12, 2024
4593796
fix: Add missing `LiteralString` import
dangotbanned Jun 12, 2024
3e07902
test: add `pytest.mark.filterwarnings` for tests that cannot avoid them
dangotbanned Jun 13, 2024
7cf20c0
refactor(typing): Replace `Literal[None]` -> `None`
dangotbanned Jun 13, 2024
65e3279
test: Change `skipif` -> `xfail` for `test_sanitize_pyarrow_table_col…
dangotbanned Jun 13, 2024
60b40a5
ci: bump `actions/setup-python`, `python-version`, use `uv` in `lint.…
dangotbanned Jun 14, 2024
ce7c95b
ci: create venv before uv pip install
dangotbanned Jun 14, 2024
50cd1e3
ci: ensure venv is activated after install
dangotbanned Jun 14, 2024
330f638
ci: use environment provided by `hatch`
dangotbanned Jun 14, 2024
237079f
ci: testing `pytest-xdist` in `build` workflow
dangotbanned Jun 14, 2024
3ec3e6a
test(perf): adds significant parallelism for slow `test_examples`
dangotbanned Jun 14, 2024
0da50cc
refactor, perf: reduce `sys.path` usage, use dictcomp in `update_init…
dangotbanned Jun 15, 2024
686a84b
build: adding debug message to help with build failure
dangotbanned Jun 15, 2024
5da0207
build: add cwd to debug message
dangotbanned Jun 15, 2024
eede0f4
fix: possibly fix cwd not on path
dangotbanned Jun 15, 2024
89ab1f3
ci: testing invoking script with `-m` for cwd
dangotbanned Jun 15, 2024
87a686c
fix: remove debug code
dangotbanned Jun 15, 2024
2122dd2
Merge branch 'update-ruff-rules' of https://github.com/dangotbanned/a…
dangotbanned Jun 15, 2024
580c9f6
Merge branch 'main' into update-ruff-rules
dangotbanned Jun 19, 2024
d0c01eb
fix: resolve lint, format, type conflicts following rebase
dangotbanned Jun 19, 2024
ca961c0
DO NOT MERGE - TESTING DOC PERF
dangotbanned Jun 21, 2024
26df622
revert: undo last commit
dangotbanned Jun 21, 2024
bb32cd3
refactor: Remove commented out dead code
dangotbanned Jun 27, 2024
89e1029
ci: Remove extra whitespace in `build.yml`
dangotbanned Jun 27, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
fi
- name: Test with pytest
run: |
pytest --doctest-modules tests
pytest --pyargs --numprocesses=logical --doctest-modules tests
- name: Validate Vega-Lite schema
run: |
# We install all 'format' dependencies of jsonschema as check-jsonschema
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/docbuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.10
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.10"
python-version: "3.12"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ jobs:
name: ruff-mypy
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.10
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.10"
python-version: "3.12"
# Installing all dependencies and not just the linters as mypy needs them for type checking
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install ".[all, dev]"
pip install hatch
- name: Lint with ruff
run: |
ruff check .
hatch run ruff check .
- name: Check formatting with ruff
run: |
ruff format --diff .
ruff format --check .
hatch run ruff format --diff .
hatch run ruff format --check .
- name: Lint with mypy
run: |
mypy altair tests
hatch run mypy altair tests
3 changes: 1 addition & 2 deletions altair/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@
"Cyclical",
"Data",
"DataFormat",
"DataFrameLike",
"DataSource",
"DataType",
"Datasets",
Expand Down Expand Up @@ -307,6 +306,7 @@
"Opacity",
"OpacityDatum",
"OpacityValue",
"Optional",
"Order",
"OrderFieldDef",
"OrderOnlyDef",
Expand Down Expand Up @@ -494,7 +494,6 @@
"TypedFieldDef",
"URI",
"Undefined",
"UndefinedType",
"UnitSpec",
"UnitSpecWithFrame",
"Url",
Expand Down
15 changes: 8 additions & 7 deletions altair/_magics.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,22 +46,22 @@ def _prepare_data(data, data_transformers):
elif isinstance(data, str):
return {"url": data}
else:
warnings.warn("data of type {} not recognized".format(type(data)), stacklevel=1)
warnings.warn(f"data of type {type(data)} not recognized", stacklevel=1)
return data


def _get_variable(name):
"""Get a variable from the notebook namespace."""
ip = IPython.get_ipython()
if ip is None:
raise ValueError(
msg = (
"Magic command must be run within an IPython "
"environment, in which get_ipython() is defined."
)
raise ValueError(msg)
if name not in ip.user_ns:
raise NameError(
"argument '{}' does not match the name of any defined variable".format(name)
)
msg = f"argument '{name}' does not match the name of any defined variable"
raise NameError(msg)
return ip.user_ns[name]


Expand Down Expand Up @@ -96,10 +96,11 @@ def vegalite(line, cell):
try:
spec = json.loads(cell)
except json.JSONDecodeError as err:
raise ValueError(
msg = (
"%%vegalite: spec is not valid JSON. "
"Install pyyaml to parse spec as yaml"
) from err
)
raise ValueError(msg) from err
else:
spec = yaml.load(cell, Loader=yaml.SafeLoader)

Expand Down
4 changes: 2 additions & 2 deletions altair/expr/consts.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict
from __future__ import annotations

from .core import ConstExpression

Expand All @@ -15,7 +15,7 @@
"PI": "the transcendental number pi (alias to Math.PI)",
}

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


def _populate_namespace():
Expand Down
60 changes: 30 additions & 30 deletions altair/expr/core.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
from __future__ import annotations
from typing import Any
from ..utils import SchemaBase


class DatumType:
"""An object to assist in building Vega-Lite Expressions"""

def __repr__(self):
def __repr__(self) -> str:
return "datum"

def __getattr__(self, attr):
def __getattr__(self, attr) -> GetAttrExpression:
if attr.startswith("__") and attr.endswith("__"):
raise AttributeError(attr)
return GetAttrExpression("datum", attr)

def __getitem__(self, attr):
def __getitem__(self, attr) -> GetItemExpression:
return GetItemExpression("datum", attr)

def __call__(self, datum, **kwargs):
def __call__(self, datum, **kwargs) -> dict[str, Any]:
"""Specify a datum for use in an encoding"""
return dict(datum=datum, **kwargs)


datum = DatumType()


def _js_repr(val):
def _js_repr(val) -> str:
"""Return a javascript-safe string representation of val"""
if val is True:
return "true"
Expand All @@ -39,10 +41,10 @@ def _js_repr(val):

# Designed to work with Expression and VariableParameter
class OperatorMixin:
def _to_expr(self):
def _to_expr(self) -> str:
return repr(self)

def _from_expr(self, expr):
def _from_expr(self, expr) -> Any:
return expr

def __add__(self, other):
Expand Down Expand Up @@ -173,7 +175,7 @@ class Expression(OperatorMixin, SchemaBase):
def to_dict(self, *args, **kwargs):
return repr(self)

def __setattr__(self, attr, val):
def __setattr__(self, attr, val) -> None:
# We don't need the setattr magic defined in SchemaBase
return object.__setattr__(self, attr, val)

Expand All @@ -183,52 +185,50 @@ def __getitem__(self, val):


class UnaryExpression(Expression):
def __init__(self, op, val):
super(UnaryExpression, self).__init__(op=op, val=val)
def __init__(self, op, val) -> None:
super().__init__(op=op, val=val)

def __repr__(self):
return "({op}{val})".format(op=self.op, val=_js_repr(self.val))
return f"({self.op}{_js_repr(self.val)})"


class BinaryExpression(Expression):
def __init__(self, op, lhs, rhs):
super(BinaryExpression, self).__init__(op=op, lhs=lhs, rhs=rhs)
def __init__(self, op, lhs, rhs) -> None:
super().__init__(op=op, lhs=lhs, rhs=rhs)

def __repr__(self):
return "({lhs} {op} {rhs})".format(
op=self.op, lhs=_js_repr(self.lhs), rhs=_js_repr(self.rhs)
)
return f"({_js_repr(self.lhs)} {self.op} {_js_repr(self.rhs)})"


class FunctionExpression(Expression):
def __init__(self, name, args):
super(FunctionExpression, self).__init__(name=name, args=args)
def __init__(self, name, args) -> None:
super().__init__(name=name, args=args)

def __repr__(self):
args = ",".join(_js_repr(arg) for arg in self.args)
return "{name}({args})".format(name=self.name, args=args)
return f"{self.name}({args})"


class ConstExpression(Expression):
def __init__(self, name, doc):
self.__doc__ = """{}: {}""".format(name, doc)
super(ConstExpression, self).__init__(name=name, doc=doc)
def __init__(self, name, doc) -> None:
self.__doc__ = f"""{name}: {doc}"""
super().__init__(name=name, doc=doc)

def __repr__(self):
def __repr__(self) -> str:
return str(self.name)


class GetAttrExpression(Expression):
def __init__(self, group, name):
super(GetAttrExpression, self).__init__(group=group, name=name)
def __init__(self, group, name) -> None:
super().__init__(group=group, name=name)

def __repr__(self):
return "{}.{}".format(self.group, self.name)
return f"{self.group}.{self.name}"


class GetItemExpression(Expression):
def __init__(self, group, name):
super(GetItemExpression, self).__init__(group=group, name=name)
def __init__(self, group, name) -> None:
super().__init__(group=group, name=name)

def __repr__(self):
return "{}[{!r}]".format(self.group, self.name)
def __repr__(self) -> str:
return f"{self.group}[{self.name!r}]"
17 changes: 11 additions & 6 deletions altair/expr/funcs.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from .core import FunctionExpression

if TYPE_CHECKING:
from typing import Iterator


FUNCTION_LISTING = {
"isArray": r"Returns true if _value_ is an array, false otherwise.",
Expand Down Expand Up @@ -169,19 +174,19 @@


class ExprFunc:
def __init__(self, name, doc):
def __init__(self, name, doc) -> None:
self.name = name
self.doc = doc
self.__doc__ = """{}(*args)\n {}""".format(name, doc)
self.__doc__ = f"""{name}(*args)\n {doc}"""

def __call__(self, *args):
def __call__(self, *args) -> FunctionExpression:
return FunctionExpression(self.name, args)

def __repr__(self):
return "<function expr.{}(*args)>".format(self.name)
def __repr__(self) -> str:
return f"<function expr.{self.name}(*args)>"


def _populate_namespace():
def _populate_namespace() -> Iterator[str]:
globals_ = globals()
for name, doc in FUNCTION_LISTING.items():
py_name = NAME_MAP.get(name, name)
Expand Down
3 changes: 2 additions & 1 deletion altair/jupyter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
# when anywidget is not installed
class JupyterChart:
def __init__(self, *args, **kwargs):
raise ImportError(
msg = (
"The Altair JupyterChart requires the anywidget \n"
"Python package which may be installed using pip with\n"
" pip install anywidget\n"
"or using conda with\n"
" conda install -c conda-forge anywidget\n"
"Afterwards, you will need to restart your Python kernel."
)
raise ImportError(msg)

else:
from .jupyter_chart import JupyterChart # noqa: F401
16 changes: 10 additions & 6 deletions altair/jupyter/jupyter_chart.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from __future__ import annotations
import json
import anywidget
import traitlets
import pathlib
from typing import Any, Set, Optional
from typing import Any

import altair as alt
from altair.utils._vegafusion_data import (
Expand Down Expand Up @@ -61,7 +62,8 @@ def __init__(self, trait_values):
elif isinstance(value, IntervalSelection):
traitlet_type = traitlets.Instance(IntervalSelection)
else:
raise ValueError(f"Unexpected selection type: {type(value)}")
msg = f"Unexpected selection type: {type(value)}"
raise ValueError(msg)

# Add the new trait.
self.add_traits(**{key: traitlet_type})
Expand All @@ -82,10 +84,11 @@ def _make_read_only(self, change):
"""
if change["name"] in self.traits() and change["old"] != change["new"]:
self._set_value(change["name"], change["old"])
raise ValueError(
msg = (
"Selections may not be set from Python.\n"
f"Attempted to set select: {change['name']}"
)
raise ValueError(msg)

def _set_value(self, key, value):
self.unobserve(self._make_read_only, names=key)
Expand Down Expand Up @@ -185,7 +188,7 @@ def __init__(
debounce_wait: int = 10,
max_wait: bool = True,
debug: bool = False,
embed_options: Optional[dict] = None,
embed_options: dict | None = None,
**kwargs: Any,
):
"""
Expand Down Expand Up @@ -278,7 +281,8 @@ def _on_change_chart(self, change):
name=clean_name, value={}, store=[]
)
else:
raise ValueError(f"Unexpected selection type {select.type}")
msg = f"Unexpected selection type {select.type}"
raise ValueError(msg)
selection_watches.append(clean_name)
initial_vl_selections[clean_name] = {"value": None, "store": []}
else:
Expand Down Expand Up @@ -384,7 +388,7 @@ def _on_change_selections(self, change):
)


def collect_transform_params(chart: TopLevelSpec) -> Set[str]:
def collect_transform_params(chart: TopLevelSpec) -> set[str]:
"""
Collect the names of params that are defined by transforms

Expand Down
Loading