Skip to content

Commit

Permalink
refactor: Rewrite versioning around an class
Browse files Browse the repository at this point in the history
Single entrypoint for both `pyproject.toml` and `vl_convert` sourced versions
  • Loading branch information
dangotbanned committed Dec 23, 2024
1 parent dc0215c commit 5aec51e
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 67 deletions.
14 changes: 4 additions & 10 deletions tools/generate_schema_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
spell_literal,
)
from tools.vega_expr import write_expr_module
from tools.versioning import VERSIONS, inline_versions_literal
from tools.versioning import VERSIONS

if TYPE_CHECKING:
from collections.abc import Iterable, Iterator
Expand Down Expand Up @@ -633,7 +633,7 @@ def copy_schemapi_util() -> None:
destination_fp.open("w", encoding="utf8") as dest,
):
dest.write(HEADER_COMMENT)
dest.writelines(chain(source.readlines(), inline_versions_literal("VERSIONS")))
dest.writelines(chain(source.readlines(), VERSIONS.iter_inline_literal()))
ruff.format(destination_fp)


Expand Down Expand Up @@ -1377,23 +1377,17 @@ def generate_encoding_artifacts(


def main() -> None:
from tools import versioning

parser = argparse.ArgumentParser(
prog="generate_schema_wrapper.py", description="Generate the Altair package."
)
parser.add_argument(
"--skip-download", action="store_true", help="skip downloading schema files"
)
args = parser.parse_args()
versioning.update_all_versions()
VERSIONS.update_all()
copy_schemapi_util()
vegalite_main(args.skip_download)
write_expr_module(
vlc.get_vega_version(),
output=EXPR_FILE,
header=HEADER_COMMENT,
)
write_expr_module(VERSIONS.vlc_vega, output=EXPR_FILE, header=HEADER_COMMENT)

# The modules below are imported after the generation of the new schema files
# as these modules import Altair. This allows them to use the new changes
Expand Down
199 changes: 142 additions & 57 deletions tools/versioning.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import sys
from collections import deque
from pathlib import Path
from typing import TYPE_CHECKING, Any, Literal
from typing import TYPE_CHECKING, Any, ClassVar, Literal

if sys.version_info >= (3, 11):
import tomllib
Expand All @@ -38,7 +38,14 @@
from tools.schemapi.utils import spell_literal

if TYPE_CHECKING:
from collections.abc import Iterable, Mapping, Sequence
from collections.abc import (
ItemsView,
Iterable,
Iterator,
KeysView,
Mapping,
Sequence,
)

if sys.version_info >= (3, 11):
from typing import LiteralString
Expand All @@ -49,20 +56,22 @@
else:
from typing_extensions import TypeAlias

__all__ = ["VERSIONS", "update_all_versions"]
__all__ = ["VERSIONS"]

_TABLE_PATH: Sequence[LiteralString] = "tool", "altair", "vega"
_REPO_ROOT: Path = Path(__file__).parent.parent
_JUPYTER_INDEX = "altair/jupyter/js/index.js"
_PYPROJECT: Literal["pyproject.toml"] = "pyproject.toml"

VegaProject: TypeAlias = Literal[
"vega-datasets", "vega-embed", "vega-lite", "vegafusion", "vl-convert-python"
]
VERSIONS: Mapping[VegaProject, str]

VERSIONS: _Versions
"""Singleton ``_Versions`` instance."""


def _read_pyproject_toml(fp: Path | None = None, /) -> dict[str, Any]:
source = fp or Path(__file__).parent.parent / "pyproject.toml"
source = fp or Path(__file__).parent.parent / _PYPROJECT
return tomllib.loads(source.read_text("utf-8"))


Expand All @@ -74,63 +83,139 @@ def _keypath(mapping: Mapping[str, Any], path: Iterable[str], /) -> Any:
return mut


def update_vega_embed() -> None:
"""Updates the **Vega-Lite** version used in ``JupyterChart``."""
fp = _REPO_ROOT / _JUPYTER_INDEX
embed = VERSIONS["vega-embed"]
vega = parse_version(vlc.get_vega_version()).major
lite = VERSIONS["vega-lite"].lstrip("v")
stmt = f'import vegaEmbed from "https://esm.sh/vega-embed@{embed}?deps=vega@{vega}&deps=vega-lite@{lite}";\n'

with fp.open("r", encoding="utf-8", newline="\n") as f:
lines = deque(f.readlines())
lines.popleft()
print(f"Updating import in {fp.as_posix()!r}, to:\n {stmt!r}")
lines.appendleft(stmt)
with fp.open("w", encoding="utf-8", newline="\n") as f:
f.writelines(lines)


def inline_versions_literal(name: str, /) -> Iterable[str]:
class _Versions:
_TABLE_PATH: ClassVar[Sequence[LiteralString]] = "tool", "altair", "vega"
"""
Yields the ``[tool.altair.vega]`` table as an inline ``dict``.
Includes a type annotation and docstring.
The table header path split by ``"."``::
Parameters
----------
name
Variable name for the literal.
Notes
-----
- Write at the bottom of ``altair.utils.schemapi``.
- Used in ``altair.utils._importers``.
[tool.altair.vega] -> "tool", "altair", "vega"
"""
ann = f"Mapping[{spell_literal(VERSIONS)}, str]"
table = f"[{'.'.join(_TABLE_PATH)}]"
yield f"{name}: {ann} = {VERSIONS!r}\n"
yield '"""\n'
yield (
"Version pins for non-``python`` `vega projects`_.\n\n"
"Notes\n"
"-----\n"
f"When cutting a new release, make sure to update ``{table}`` in ``pyproject.toml``.\n\n"
".. _vega projects:\n"
" https://github.com/vega\n"
)
yield '"""\n'


def update_all_versions() -> None:
print("Updating Vega project pins")
update_vega_embed()


def __getattr__(name: str) -> Mapping[VegaProject, str]:
_CONST_NAME: ClassVar[Literal["VERSIONS"]] = "VERSIONS"
"""Variable name for the exported literal."""

_mapping: Mapping[VegaProject, str]

def __init__(self) -> None:
self._mapping = _keypath(_read_pyproject_toml(), self._TABLE_PATH)

def __getitem__(self, key: VegaProject) -> str:
return self._mapping[key]

def __repr__(self) -> str:
return repr(self._mapping)

def projects(self) -> KeysView[VegaProject]:
return self._mapping.keys()

def items(self) -> ItemsView[VegaProject, str]:
return self._mapping.items()

@property
def vlc_vega(self) -> str:
"""
Returns version of `Vega`_ bundled with `vl-convert`_.
.. _Vega:
https://github.com/vega/vega
.. _vl-convert:
https://github.com/vega/vl-convert
"""
return vlc.get_vega_version()

@property
def vlc_vega_embed(self) -> str:
"""
Returns version of `Vega-Embed`_ bundled with `vl-convert`_.
.. _Vega-Embed:
https://github.com/vega/vega-embed
.. _vl-convert:
https://github.com/vega/vl-convert
"""
return vlc.get_vega_embed_version()

@property
def vlc_vega_themes(self) -> str:
"""
Returns version of `Vega-Themes`_ bundled with `vl-convert`_.
.. _Vega-Themes:
https://github.com/vega/vega-themes
.. _vl-convert:
https://github.com/vega/vl-convert.
"""
return vlc.get_vega_themes_version()

@property
def vlc_vegalite(self) -> list[str]:
"""
Returns versions of `Vega-Lite`_ bundled with `vl-convert`_.
.. _Vega-Lite:
https://github.com/vega/vega-lite
.. _vl-convert:
https://github.com/vega/vl-convert
"""
return vlc.get_vegalite_versions()

@property
def _annotation(self) -> str:
return f"Mapping[{spell_literal(self.projects())}, str]"

@property
def _header(self) -> str:
return f"[{'.'.join(self._TABLE_PATH)}]"

def iter_inline_literal(self) -> Iterator[str]:
"""
Yields the ``[tool.altair.vega]`` table as an inline ``dict``.
Includes a type annotation and docstring.
Notes
-----
- Write at the bottom of ``altair.utils.schemapi``.
- Used in ``altair.utils._importers``.
"""
yield f"{self._CONST_NAME}: {self._annotation} = {self!r}\n"
yield '"""\n'
yield (
"Version pins for non-``python`` `vega projects`_.\n\n"
"Notes\n"
"-----\n"
f"When cutting a new release, make sure to update ``{self._header}`` in ``pyproject.toml``.\n\n"
".. _vega projects:\n"
" https://github.com/vega\n"
)
yield '"""\n'

def update_all(self) -> None:
"""Update all static version pins."""
print("Updating Vega project pins")
self.update_vega_embed()

def update_vega_embed(self) -> None:
"""Updates the **Vega-Lite** version used in ``JupyterChart``."""
fp = _REPO_ROOT / _JUPYTER_INDEX
embed = self["vega-embed"]
vega = parse_version(self.vlc_vega).major
vegalite = self["vega-lite"].lstrip("v")
stmt = f'import vegaEmbed from "https://esm.sh/vega-embed@{embed}?deps=vega@{vega}&deps=vega-lite@{vegalite}";\n'

with fp.open("r", encoding="utf-8", newline="\n") as f:
lines = deque(f.readlines())
lines.popleft()
print(f"Updating import in {fp.as_posix()!r}, to:\n {stmt!r}")
lines.appendleft(stmt)
with fp.open("w", encoding="utf-8", newline="\n") as f:
f.writelines(lines)


def __getattr__(name: str) -> _Versions:
if name == "VERSIONS":
global VERSIONS
VERSIONS = _keypath(_read_pyproject_toml(), _TABLE_PATH)
VERSIONS = _Versions()
return VERSIONS
else:
msg = f"module {__name__!r} has no attribute {name!r}"
Expand Down

0 comments on commit 5aec51e

Please sign in to comment.