diff --git a/RELEASING.md b/RELEASING.md index a3de236e4..6582af76a 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -2,24 +2,26 @@ Remove any existing environments managed by `hatch` so that it will create new ones with the latest dependencies when executing the commands further below: - hatch env prune + hatch env prune -2. Make certain your branch is in sync with head. If you work on a fork, replace `origin` with `upstream`: +2. Make certain your branch is in sync with head, and that you have no uncommitted modifications. If you work on a fork, replace `origin` with `upstream`: - git pull origin main + git checkout main + git pull origin main + git status # Should show "nothing to commit, working tree clean" 3. Do a clean doc build: - hatch run doc:clean-all - hatch run doc:build-html - hatch run doc:serve + hatch run doc:clean-all + hatch run doc:build-html + hatch run doc:serve Navigate to http://localhost:8000 and ensure it looks OK (particularly do a visual scan of the gallery thumbnails). 4. Create a new release branch: - git switch -c version_5.0.0 + git switch -c version_5.0.0 5. Update version to, e.g. 5.0.0: @@ -28,56 +30,51 @@ 6. Commit changes and push: - git add . -u - git commit -m "chore: Bump version to 5.0.0" - git push + git add . -u + git commit -m "chore: Bump version to 5.0.0" + git push 7. Merge release branch into main, make sure that all required checks pass -8. Tag the release: - - git tag -a v5.0.0 -m "version 5.0.0 release" - git push origin v5.0.0 - -9. On main, build source & wheel distributions. If you work on a fork, replace `origin` with `upstream`: +8. On main, build source & wheel distributions. If you work on a fork, replace `origin` with `upstream`: - git switch main - git pull origin main - hatch clean # clean old builds & distributions - hatch build # create a source distribution and universal wheel + git switch main + git pull origin main + hatch clean # clean old builds & distributions + hatch build # create a source distribution and universal wheel -10. publish to PyPI (Requires correct PyPI owner permissions): +9. publish to PyPI (Requires correct PyPI owner permissions): hatch publish -11. build and publish docs (Requires write-access to altair-viz/altair-viz.github.io): +10. build and publish docs (Requires write-access to altair-viz/altair-viz.github.io): hatch run doc:publish-clean-build -12. On main, tag the release. If you work on a fork, replace `origin` with `upstream`: +11. On main, tag the release. If you work on a fork, replace `origin` with `upstream`: - git tag -a v5.0.0 -m "Version 5.0.0 release" - git push origin v5.0.0 + git tag -a v5.0.0 -m "Version 5.0.0 release" + git push origin v5.0.0 -13. Create a new branch: +12. Create a new branch: git switch -c maint_5.1.0dev -14. Update version and add 'dev' suffix, e.g. 5.1.0dev: +13. Update version and add 'dev' suffix, e.g. 5.1.0dev: - in ``altair/__init__.py`` - in ``doc/conf.py`` -15. Commit changes and push: +14. Commit changes and push: git add . -u git commit -m "chore: Bump version to 5.1.0dev" git push -16. Merge maintenance branch into main +15. Merge maintenance branch into main -17. Double-check that a conda-forge pull request is generated from the updated +16. Double-check that a conda-forge pull request is generated from the updated pip package by the conda-forge bot (may take up to several hours): https://github.com/conda-forge/altair-feedstock/pulls -18. Publish a new release in https://github.com/vega/altair/releases/ +17. Publish a new release in https://github.com/vega/altair/releases/ diff --git a/altair/__init__.py b/altair/__init__.py index 279cfec67..2c0904647 100644 --- a/altair/__init__.py +++ b/altair/__init__.py @@ -1,5 +1,5 @@ # ruff: noqa -__version__ = "5.5.0dev" +__version__ = "5.5.0" # The content of __all__ is automatically written by # tools/update_init_file.py. Do not modify directly. diff --git a/altair/expr/__init__.py b/altair/expr/__init__.py index 38d87f4c5..17e69357b 100644 --- a/altair/expr/__init__.py +++ b/altair/expr/__init__.py @@ -117,6 +117,115 @@ class expr(_ExprRef, metaclass=_ExprMeta): }))}, shorthand: 'yval' }) + + .. _Number.isNaN: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNan + .. _Number.isFinite: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite + .. _Math.abs: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs + .. _Math.acos: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acos + .. _Math.asin: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asin + .. _Math.atan: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan + .. _Math.atan2: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2 + .. _Math.ceil: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil + .. _Math.cos: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cos + .. _Math.exp: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/exp + .. _Math.floor: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor + .. _Math.hypot: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/hypot + .. _Math.log: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log + .. _Math.max: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max + .. _Math.min: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min + .. _Math.pow: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow + .. _Math.random: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random + .. _Math.round: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round + .. _Math.sin: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sin + .. _Math.sqrt: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt + .. _Math.tan: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tan + .. _normal (Gaussian) probability distribution: + https://en.wikipedia.org/wiki/Normal_distribution + .. _cumulative distribution function: + https://en.wikipedia.org/wiki/Cumulative_distribution_function + .. _probability density function: + https://en.wikipedia.org/wiki/Probability_density_function + .. _log-normal probability distribution: + https://en.wikipedia.org/wiki/Log-normal_distribution + .. _continuous uniform probability distribution: + https://en.wikipedia.org/wiki/Continuous_uniform_distribution + .. _*unit*: + https://vega.github.io/vega/docs/api/time/#time-units + .. _JavaScript's String.replace: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace + .. _d3-format specifier: + https://github.com/d3/d3-format/ + .. _*units*: + https://vega.github.io/vega/docs/api/time/#time-units + .. _timeUnitSpecifier API documentation: + https://vega.github.io/vega/docs/api/time/#timeUnitSpecifier + .. _timeFormat: + https://vega.github.io/vega/docs/expressions/#timeFormat + .. _utcFormat: + https://vega.github.io/vega/docs/expressions/#utcFormat + .. _d3-time-format specifier: + https://github.com/d3/d3-time-format/ + .. _TimeMultiFormat object: + https://vega.github.io/vega/docs/types/#TimeMultiFormat + .. _UTC: + https://en.wikipedia.org/wiki/Coordinated_Universal_Time + .. _JavaScript's RegExp: + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp + .. _RGB: + https://en.wikipedia.org/wiki/RGB_color_model + .. _d3-color's rgb function: + https://github.com/d3/d3-color#rgb + .. _HSL: + https://en.wikipedia.org/wiki/HSL_and_HSV + .. _d3-color's hsl function: + https://github.com/d3/d3-color#hsl + .. _CIE LAB: + https://en.wikipedia.org/wiki/Lab_color_space#CIELAB + .. _d3-color's lab function: + https://github.com/d3/d3-color#lab + .. _HCL: + https://en.wikipedia.org/wiki/Lab_color_space#CIELAB + .. _d3-color's hcl function: + https://github.com/d3/d3-color#hcl + .. _W3C Web Content Accessibility Guidelines: + https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef + .. _continuous color scheme: + https://vega.github.io/vega/docs/schemes + .. _geoArea: + https://github.com/d3/d3-geo#geoArea + .. _path.area: + https://github.com/d3/d3-geo#path_area + .. _geoBounds: + https://github.com/d3/d3-geo#geoBounds + .. _path.bounds: + https://github.com/d3/d3-geo#path_bounds + .. _geoCentroid: + https://github.com/d3/d3-geo#geoCentroid + .. _path.centroid: + https://github.com/d3/d3-geo#path_centroid + .. _window.screen: + https://developer.mozilla.org/en-US/docs/Web/API/Window/screen """ @override @@ -643,13 +752,13 @@ def sampleUniform( cls, min: IntoExpression = None, max: IntoExpression = None, / ) -> Expression: """ - Returns a sample from a univariate `continuous uniform probability distribution`_) over the interval [``min``, ``max``). + Returns a sample from a univariate `continuous uniform probability distribution`_ over the interval [``min``, ``max``). If unspecified, ``min`` defaults to ``0`` and ``max`` defaults to ``1``. If only one argument is provided, it is interpreted as the ``max`` value. .. _continuous uniform probability distribution: - https://en.wikipedia.org/wiki/Uniform_distribution_(continuous + https://en.wikipedia.org/wiki/Continuous_uniform_distribution """ return FunctionExpression("sampleUniform", (min, max)) diff --git a/altair/vegalite/v5/schema/channels.py b/altair/vegalite/v5/schema/channels.py index 3562bc83e..a4a676c23 100644 --- a/altair/vegalite/v5/schema/channels.py +++ b/altair/vegalite/v5/schema/channels.py @@ -8308,8 +8308,8 @@ class Radius(FieldChannelMixin, core.PositionFieldDefBase): stacked bar and area charts `__ and pie charts `with percentage tooltip - `__). :raw-html:`
` - -``"center"`` - stacking with center baseline (for `streamgraph + `__). + * ``"center"`` - stacking with center baseline (for `streamgraph `__). * ``null`` or ``false`` - No-stacking. This will produce layered `bar `__ and area @@ -8644,8 +8644,8 @@ class RadiusDatum(DatumChannelMixin, core.PositionDatumDefBase): stacked bar and area charts `__ and pie charts `with percentage tooltip - `__). :raw-html:`
` - -``"center"`` - stacking with center baseline (for `streamgraph + `__). + * ``"center"`` - stacking with center baseline (for `streamgraph `__). * ``null`` or ``false`` - No-stacking. This will produce layered `bar `__ and area @@ -15197,8 +15197,8 @@ class Theta(FieldChannelMixin, core.PositionFieldDefBase): stacked bar and area charts `__ and pie charts `with percentage tooltip - `__). :raw-html:`
` - -``"center"`` - stacking with center baseline (for `streamgraph + `__). + * ``"center"`` - stacking with center baseline (for `streamgraph `__). * ``null`` or ``false`` - No-stacking. This will produce layered `bar `__ and area @@ -15529,8 +15529,8 @@ class ThetaDatum(DatumChannelMixin, core.PositionDatumDefBase): stacked bar and area charts `__ and pie charts `with percentage tooltip - `__). :raw-html:`
` - -``"center"`` - stacking with center baseline (for `streamgraph + `__). + * ``"center"`` - stacking with center baseline (for `streamgraph `__). * ``null`` or ``false`` - No-stacking. This will produce layered `bar `__ and area @@ -17067,8 +17067,8 @@ class X(FieldChannelMixin, core.PositionFieldDef): stacked bar and area charts `__ and pie charts `with percentage tooltip - `__). :raw-html:`
` - -``"center"`` - stacking with center baseline (for `streamgraph + `__). + * ``"center"`` - stacking with center baseline (for `streamgraph `__). * ``null`` or ``false`` - No-stacking. This will produce layered `bar `__ and area @@ -17550,8 +17550,8 @@ class XDatum(DatumChannelMixin, core.PositionDatumDef): stacked bar and area charts `__ and pie charts `with percentage tooltip - `__). :raw-html:`
` - -``"center"`` - stacking with center baseline (for `streamgraph + `__). + * ``"center"`` - stacking with center baseline (for `streamgraph `__). * ``null`` or ``false`` - No-stacking. This will produce layered `bar `__ and area @@ -19306,8 +19306,8 @@ class Y(FieldChannelMixin, core.PositionFieldDef): stacked bar and area charts `__ and pie charts `with percentage tooltip - `__). :raw-html:`
` - -``"center"`` - stacking with center baseline (for `streamgraph + `__). + * ``"center"`` - stacking with center baseline (for `streamgraph `__). * ``null`` or ``false`` - No-stacking. This will produce layered `bar `__ and area @@ -19789,8 +19789,8 @@ class YDatum(DatumChannelMixin, core.PositionDatumDef): stacked bar and area charts `__ and pie charts `with percentage tooltip - `__). :raw-html:`
` - -``"center"`` - stacking with center baseline (for `streamgraph + `__). + * ``"center"`` - stacking with center baseline (for `streamgraph `__). * ``null`` or ``false`` - No-stacking. This will produce layered `bar `__ and area diff --git a/altair/vegalite/v5/schema/core.py b/altair/vegalite/v5/schema/core.py index 51ae8ab5d..fa2df587a 100644 --- a/altair/vegalite/v5/schema/core.py +++ b/altair/vegalite/v5/schema/core.py @@ -15216,8 +15216,8 @@ class PositionDatumDefBase(PolarDef): stacked bar and area charts `__ and pie charts `with percentage tooltip - `__). :raw-html:`
` - -``"center"`` - stacking with center baseline (for `streamgraph + `__). + * ``"center"`` - stacking with center baseline (for `streamgraph `__). * ``null`` or ``false`` - No-stacking. This will produce layered `bar `__ and area @@ -15410,8 +15410,8 @@ class PositionDatumDef(PositionDef): stacked bar and area charts `__ and pie charts `with percentage tooltip - `__). :raw-html:`
` - -``"center"`` - stacking with center baseline (for `streamgraph + `__). + * ``"center"`` - stacking with center baseline (for `streamgraph `__). * ``null`` or ``false`` - No-stacking. This will produce layered `bar `__ and area @@ -15681,8 +15681,8 @@ class PositionFieldDef(PositionDef): stacked bar and area charts `__ and pie charts `with percentage tooltip - `__). :raw-html:`
` - -``"center"`` - stacking with center baseline (for `streamgraph + `__). + * ``"center"`` - stacking with center baseline (for `streamgraph `__). * ``null`` or ``false`` - No-stacking. This will produce layered `bar `__ and area @@ -15963,8 +15963,8 @@ class PositionFieldDefBase(PolarDef): stacked bar and area charts `__ and pie charts `with percentage tooltip - `__). :raw-html:`
` - -``"center"`` - stacking with center baseline (for `streamgraph + `__). + * ``"center"`` - stacking with center baseline (for `streamgraph `__). * ``null`` or ``false`` - No-stacking. This will produce layered `bar `__ and area diff --git a/doc/conf.py b/doc/conf.py index a01b6b85d..b4bd6a7c0 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -79,7 +79,7 @@ # built documents. # # The short X.Y version. -version = "5.5.0dev" +version = "5.5.0" # The full version, including alpha/beta/rc tags. release = f"{version}" diff --git a/doc/user_guide/api.rst b/doc/user_guide/api.rst index d3c08f547..5793f0ae8 100644 --- a/doc/user_guide/api.rst +++ b/doc/user_guide/api.rst @@ -791,3 +791,5 @@ Typing Optional is_chart_type +.. _Generic: + https://typing.readthedocs.io/en/latest/spec/generics.html#generics diff --git a/tools/generate_api_docs.py b/tools/generate_api_docs.py index b9c2a1a2a..55c68729e 100644 --- a/tools/generate_api_docs.py +++ b/tools/generate_api_docs.py @@ -110,6 +110,8 @@ {typing_objects} +.. _Generic: + https://typing.readthedocs.io/en/latest/spec/generics.html#generics """ diff --git a/tools/markup.py b/tools/markup.py index 440efe629..88239299c 100644 --- a/tools/markup.py +++ b/tools/markup.py @@ -40,7 +40,10 @@ def __init__(self) -> None: def inline_html(self, token: Token, state: BlockState) -> str: html = token["raw"] - return rf"\ :raw-html:`{html}`\ " + if html == "
": + return "\n" + else: + return rf" :raw-html:`{html}` " class RSTParse(_Markdown): @@ -131,7 +134,9 @@ def process_text(self, text: str, state: InlineState) -> None: state.append_token({"type": "text", "raw": _RE_LIQUID_INCLUDE.sub(r"", text)}) -def read_ast_tokens(source: Url | Path, /) -> list[Token]: +def read_ast_tokens( + source: Url | Path, /, replacements: list[tuple[str, str]] | None = None +) -> list[Token]: """ Read from ``source``, drop ``BlockState``. @@ -139,11 +144,17 @@ def read_ast_tokens(source: Url | Path, /) -> list[Token]: """ markdown = _Markdown(renderer=None, inline=InlineParser()) if isinstance(source, Path): - tokens = markdown.read(source) + token_text = source.read_text() else: with request.urlopen(source) as response: - s = response.read().decode("utf-8") - tokens = markdown.parse(s, markdown.block.state_cls()) + token_text = response.read().decode("utf-8") + + # Apply replacements + if replacements: + for replacement in replacements: + token_text = token_text.replace(replacement[0], replacement[1]) + + tokens = markdown.parse(token_text, markdown.block.state_cls()) return tokens[0] diff --git a/tools/vega_expr.py b/tools/vega_expr.py index 1941f64fd..aa42e5ed6 100644 --- a/tools/vega_expr.py +++ b/tools/vega_expr.py @@ -47,6 +47,14 @@ EXPRESSIONS_DOCS_URL: LiteralString = f"{VEGA_DOCS_URL}expressions/" EXPRESSIONS_URL_TEMPLATE = "https://raw.githubusercontent.com/vega/vega/refs/tags/{version}/docs/docs/expressions.md" +# Replacements to apply prior to parsing as markdown +PRE_PARSE_REPLACEMENTS = [ + # Closing paren messes up markdown parsing, replace with equivalent wikipedia URL + ( + "https://en.wikipedia.org/wiki/Uniform_distribution_(continuous)", + "https://en.wikipedia.org/wiki/Continuous_uniform_distribution", + ) +] # NOTE: Regex patterns FUNCTION_DEF_LINE: Pattern[str] = re.compile( @@ -219,7 +227,7 @@ def PI(cls) -> {return_ann}: CLS_TEMPLATE = '''\ class expr({base}, metaclass={metaclass}): - """{doc}""" + """{doc}\n{links}""" @override def __new__(cls: type[{base}], expr: str) -> {base}: {type_ignore} @@ -443,6 +451,20 @@ def __init__(self, name: str, children: Sequence[Token], /) -> None: self.signature: str = "" self._special: set[Special] = set() + def get_links(self, rst_renderer: RSTRenderer) -> dict[str, str]: + """Retrieve dict of link text to link url.""" + from mistune import BlockState + + links = {} + state = BlockState() + for t in self._children: + if t.get("type") == "link" and (url := t.get("attrs", {}).get("url")): + text = rst_renderer.render_children(t, state) + text = text.replace("`", "") + links[text] = expand_urls(url) + + return links + def with_doc(self) -> Self: """ Parses docstring content in full. @@ -917,13 +939,15 @@ def italics_to_backticks(s: str, names: Iterable[str], /) -> str: return re.sub(pattern, r"\g``\g``\g", s) -def parse_expressions(source: Url | Path, /) -> Iterator[VegaExprDef]: +def parse_expressions( + source: Url | Path, /, replacements: list[tuple[str, str]] | None = None +) -> Iterator[VegaExprDef]: """ Download remote or read local `.md` resource and eagerly parse signatures of relevant definitions. Yields with docs to ensure each can use all remapped names, regardless of the order they appear. """ - tokens = read_ast_tokens(source) + tokens = read_ast_tokens(source, replacements=replacements) expr_defs = tuple(VegaExprDef.from_tokens(tokens)) VegaExprDef.remap_title.refresh() for expr_def in expr_defs: @@ -943,6 +967,21 @@ def write_expr_module(version: str, output: Path, *, header: str) -> None: """ version = version if version.startswith("v") else f"v{version}" url = EXPRESSIONS_URL_TEMPLATE.format(version=version) + + # Retrieve all of the links used in expr method docstrings, + # so we can include them in the class docstrings, so that sphinx + # will find them. + expr_defs = parse_expressions(url, replacements=PRE_PARSE_REPLACEMENTS) + + links = {} + rst_renderer = RSTRenderer() + for expr_def in expr_defs: + links.update(expr_def.get_links(rst_renderer)) + + links_rst = [] + for anchor, link_target in links.items(): + links_rst.append(f" .. _{anchor}:\n {link_target}") + content = ( MODULE_PRE.format( header=header, @@ -956,12 +995,16 @@ def write_expr_module(version: str, output: Path, *, header: str) -> None: base="_ExprRef", metaclass=CLS_META, doc=CLS_DOC, + links="\n".join(links_rst), type_ignore=IGNORE_MISC, ), ) contents = chain( content, - (expr_def.render() for expr_def in parse_expressions(url)), + ( + expr_def.render() + for expr_def in parse_expressions(url, replacements=PRE_PARSE_REPLACEMENTS) + ), [MODULE_POST], ) print(f"Generating\n {url!s}\n ->{output!s}")