diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index 48f723cab..c1a819029 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -14,7 +14,7 @@ jobs: strategy: matrix: # "3.10" must be a string; otherwise it is interpreted as 3.1. - python-version: ["3.11", "3.10", "3.9", "3.8", "3.7"] + python-version: ["3.11", "3.10", "3.9", "3.8"] os: [ubuntu-latest, windows-latest, macOS-latest] fail-fast: false @@ -32,7 +32,7 @@ jobs: make test - name: Type check with pyright - if: matrix.python-version != '3.7' && steps.install.outcome == 'success' && (success() || failure()) + if: steps.install.outcome == 'success' && (success() || failure()) run: | make pyright @@ -51,7 +51,7 @@ jobs: if: github.event_name != 'release' strategy: matrix: - python-version: ["3.11", "3.10", "3.9", "3.8", "3.7"] + python-version: ["3.11", "3.10", "3.9", "3.8"] os: [ubuntu-latest] fail-fast: false @@ -71,7 +71,7 @@ jobs: if: github.event_name != 'release' strategy: matrix: - python-version: ["3.11", "3.10", "3.9", "3.8", "3.7"] + python-version: ["3.11", "3.10", "3.9", "3.8"] os: [ubuntu-latest] fail-fast: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 993cbfc18..d6062841b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Other changes +* Removed Python 3.7 support. (#590) + ## [0.4.0] - 2023-06-26 diff --git a/e2e/controls.py b/e2e/controls.py index b19a482ff..8eb1e78c9 100644 --- a/e2e/controls.py +++ b/e2e/controls.py @@ -7,6 +7,7 @@ import sys import time import typing +from typing import Literal, Protocol from playwright.sync_api import FilePayload, FloatRect, Locator, Page, Position from playwright.sync_api import expect as playwright_expect @@ -14,12 +15,6 @@ # Import `shiny`'s typing extentions. # Since this is a private file, tell pyright to ignore the import # (Imports split over many import statements due to auto formatting) -from shiny._typing_extensions import ( - Literal, # pyright: ignore[reportPrivateImportUsage] -) -from shiny._typing_extensions import ( - Protocol, # pyright: ignore[reportPrivateImportUsage] -) from shiny._typing_extensions import ( TypeGuard, # pyright: ignore[reportPrivateImportUsage] ) diff --git a/e2e/inputs/test_input_date.py b/e2e/inputs/test_input_date.py index ce37ced1f..4f3180120 100644 --- a/e2e/inputs/test_input_date.py +++ b/e2e/inputs/test_input_date.py @@ -2,15 +2,12 @@ import datetime import typing +from typing import Literal from conftest import ShinyAppProc, create_doc_example_fixture from controls import InputDate from playwright.sync_api import Page, expect -from shiny._typing_extensions import ( - Literal, # pyright: ignore[reportPrivateImportUsage] -) - app = create_doc_example_fixture("input_date") diff --git a/e2e/inputs/test_input_date_range.py b/e2e/inputs/test_input_date_range.py index 1f1f5ecbf..368099da8 100644 --- a/e2e/inputs/test_input_date_range.py +++ b/e2e/inputs/test_input_date_range.py @@ -2,15 +2,12 @@ import datetime import typing +from typing import Literal from conftest import ShinyAppProc, create_doc_example_fixture from controls import InputDateRange from playwright.sync_api import Page, expect -from shiny._typing_extensions import ( - Literal, # pyright: ignore[reportPrivateImportUsage] -) - app = create_doc_example_fixture("input_date_range") diff --git a/setup.cfg b/setup.cfg index 03512558d..ee723c889 100644 --- a/setup.cfg +++ b/setup.cfg @@ -15,7 +15,6 @@ classifiers = Intended Audience :: Developers License :: OSI Approved :: MIT License Natural Language :: English - Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 @@ -26,7 +25,7 @@ project_urls = Source Code = https://github.com/rstudio/py-shiny [options] -python_requires = >=3.7 +python_requires = >=3.8 packages = find_namespace: test_suite = tests include_package_data = True @@ -50,7 +49,6 @@ install_requires = appdirs>=1.4.4 asgiref>=3.5.2 watchfiles>=0.18.0;platform_system!="Emscripten" - importlib-metadata>=1.1.0,<5;python_version<"3.8" tests_require = pytest>=3 zip_safe = False @@ -76,9 +74,7 @@ test = plotly dev = black>=23.1.0 - # Python 3.7 needs a specific version of flake8 to satisfy other dependencies. - flake8==3.9.2;python_version<="3.7" - flake8>=6.0.0;python_version>"3.7" + flake8>=6.0.0 flake8-bugbear>=23.2.13 isort>=5.10.1 # pyright produces break changes rapidly. Fix to a particular version diff --git a/shiny/_docstring.py b/shiny/_docstring.py index 903ea8193..2abd578b4 100644 --- a/shiny/_docstring.py +++ b/shiny/_docstring.py @@ -2,9 +2,7 @@ import json import os -from typing import Any, Callable, TypeVar - -from ._typing_extensions import Literal +from typing import Any, Callable, Literal, TypeVar ex_dir: str = os.path.join(os.path.dirname(os.path.abspath(__file__)), "examples") diff --git a/shiny/_typing_extensions.py b/shiny/_typing_extensions.py index 8e72db095..94d8f7c56 100644 --- a/shiny/_typing_extensions.py +++ b/shiny/_typing_extensions.py @@ -5,11 +5,6 @@ import sys -if sys.version_info >= (3, 8): - from typing import Literal, Protocol, runtime_checkable -else: - from typing_extensions import Literal, Protocol, runtime_checkable - if sys.version_info >= (3, 10): from typing import Concatenate, ParamSpec, TypeGuard else: @@ -28,4 +23,4 @@ # conditional imports into the .pyi file when generating type stubs. Without this line, # pyright will not include the above imports in the generated .pyi file, and it will # result in a lot of red squiggles in user code. -_: 'Literal | Protocol | runtime_checkable | Concatenate[str, ParamSpec("P")] | ParamSpec | TypeGuard | NotRequired | TypedDict | assert_type' # type:ignore +_: 'Concatenate[str, ParamSpec("P")] | ParamSpec | TypeGuard | NotRequired | TypedDict | assert_type' # type:ignore diff --git a/shiny/_utils.py b/shiny/_utils.py index 43ff2f5da..5c471143f 100644 --- a/shiny/_utils.py +++ b/shiny/_utils.py @@ -10,16 +10,12 @@ import random import secrets import socketserver -import sys import tempfile from typing import Any, Awaitable, Callable, Optional, TypeVar, cast from ._typing_extensions import ParamSpec, TypeGuard -if sys.version_info >= (3, 8): - CancelledError = asyncio.CancelledError -else: - CancelledError = asyncio.futures.CancelledError +CancelledError = asyncio.CancelledError # ============================================================================== @@ -62,12 +58,8 @@ def guess_mime_type( Guess the MIME type of a file. This is a wrapper for mimetypes.guess_type, but it only returns the type (and not encoding), and it allows a default value. """ - # In Python<=3.7, mimetypes.guess_type only accepts strings. - # # Note that in the parameters above, "os.PathLike[str]" is in quotes to avoid # "TypeError: 'ABCMeta' object is not subscriptable", in Python<=3.8. - if sys.version_info < (3, 8): - url = os.fspath(url) return mimetypes.guess_type(url, strict)[0] or default diff --git a/shiny/experimental/ui/_accordion.py b/shiny/experimental/ui/_accordion.py index 8158813d7..246d771a6 100644 --- a/shiny/experimental/ui/_accordion.py +++ b/shiny/experimental/ui/_accordion.py @@ -1,12 +1,11 @@ from __future__ import annotations import random -from typing import Optional, TypeVar +from typing import Literal, Optional, TypeVar from htmltools import Tag, TagAttrs, TagAttrValue, TagChild, css, tags from ... import Session -from ..._typing_extensions import Literal from ..._utils import drop_none from ...session import require_active_session from ...types import MISSING, MISSING_TYPE diff --git a/shiny/experimental/ui/_card.py b/shiny/experimental/ui/_card.py index 9fdf28a99..d6e00640d 100644 --- a/shiny/experimental/ui/_card.py +++ b/shiny/experimental/ui/_card.py @@ -4,7 +4,7 @@ import io import mimetypes from pathlib import Path, PurePath -from typing import Optional +from typing import Literal, Optional, Protocol from htmltools import ( HTML, @@ -19,7 +19,6 @@ tags, ) -from ..._typing_extensions import Literal, Protocol from ...types import MISSING, MISSING_TYPE from ._css_unit import CssUnit, as_css_padding, as_css_unit from ._fill import as_fill_carrier, as_fill_item, as_fillable_container diff --git a/shiny/experimental/ui/_fill.py b/shiny/experimental/ui/_fill.py index 2b11bb4cc..84a68d4b1 100644 --- a/shiny/experimental/ui/_fill.py +++ b/shiny/experimental/ui/_fill.py @@ -1,10 +1,9 @@ from __future__ import annotations -from typing import Optional, TypeVar +from typing import Literal, Optional, Protocol, TypeVar, runtime_checkable from htmltools import Tag, TagChild, Tagifiable, css -from ..._typing_extensions import Literal, Protocol, runtime_checkable from ._css_unit import CssUnit, as_css_unit from ._htmldeps import fill_dependency from ._tag import tag_add_style, tag_prepend_class, tag_remove_class diff --git a/shiny/experimental/ui/_input_text.py b/shiny/experimental/ui/_input_text.py index 609bae751..6d4f8a7ef 100644 --- a/shiny/experimental/ui/_input_text.py +++ b/shiny/experimental/ui/_input_text.py @@ -1,12 +1,11 @@ __all__ = ("input_text_area",) -from typing import Optional +from typing import Literal, Optional from htmltools import Tag, TagChild, css, div, tags # from ..._docstring import add_example from ..._namespaces import resolve_id -from ..._typing_extensions import Literal from ...ui._utils import shiny_input_label from ._htmldeps import autoresize_dependency diff --git a/shiny/experimental/ui/_layout.py b/shiny/experimental/ui/_layout.py index 062e4efe6..6bf6085fe 100644 --- a/shiny/experimental/ui/_layout.py +++ b/shiny/experimental/ui/_layout.py @@ -1,10 +1,9 @@ from __future__ import annotations -from typing import Optional +from typing import Literal, Optional from htmltools import TagAttrs, TagAttrValue, TagChild, css, div -from ..._typing_extensions import Literal from ._css_unit import CssUnit, as_css_unit from ._fill import as_fill_item, as_fillable_container from ._utils import consolidate_attrs, is_01_scalar diff --git a/shiny/experimental/ui/_navs.py b/shiny/experimental/ui/_navs.py index 13fbc2bec..38c487b68 100644 --- a/shiny/experimental/ui/_navs.py +++ b/shiny/experimental/ui/_navs.py @@ -7,12 +7,11 @@ ) import copy -from typing import Any, Optional, Sequence, cast +from typing import Any, Literal, Optional, Sequence, cast from htmltools import MetadataNode, Tag, TagChild, TagList, css, div, tags from ..._namespaces import resolve_id -from ..._typing_extensions import Literal from ..._utils import private_random_int from ...types import NavSetArg from ...ui._html_dependencies import bootstrap_deps diff --git a/shiny/experimental/ui/_page.py b/shiny/experimental/ui/_page.py index 8fa307133..a9251f750 100644 --- a/shiny/experimental/ui/_page.py +++ b/shiny/experimental/ui/_page.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Optional, Sequence +from typing import Literal, Optional, Sequence from htmltools import ( MetadataNode, @@ -13,7 +13,6 @@ tags, ) -from ..._typing_extensions import Literal from ...types import MISSING, MISSING_TYPE, NavSetArg from ...ui._page import page_bootstrap from ...ui._utils import get_window_title diff --git a/shiny/experimental/ui/_sidebar.py b/shiny/experimental/ui/_sidebar.py index 88842c328..bf6ed668d 100644 --- a/shiny/experimental/ui/_sidebar.py +++ b/shiny/experimental/ui/_sidebar.py @@ -1,7 +1,7 @@ from __future__ import annotations import random -from typing import Optional +from typing import Literal, Optional from htmltools import Tag, TagAttrs, TagAttrValue, TagChild, TagList, css, div from htmltools import svg as svgtags @@ -9,7 +9,6 @@ from ... import Session from ..._deprecated import warn_deprecated -from ..._typing_extensions import Literal from ...session import require_active_session # from ._color import get_color_contrast diff --git a/shiny/plotutils.py b/shiny/plotutils.py index 9311b7208..05c3f1fad 100644 --- a/shiny/plotutils.py +++ b/shiny/plotutils.py @@ -7,9 +7,9 @@ __all__ = ("brushed_points", "near_points") -from typing import TYPE_CHECKING, Optional, Union, cast +from typing import TYPE_CHECKING, Literal, Optional, Union, cast -from ._typing_extensions import Literal, TypedDict +from ._typing_extensions import TypedDict from .types import BrushInfo, CoordInfo, CoordXY if TYPE_CHECKING: diff --git a/shiny/render/_dataframe.py b/shiny/render/_dataframe.py index 84bb20031..567503292 100644 --- a/shiny/render/_dataframe.py +++ b/shiny/render/_dataframe.py @@ -8,15 +8,17 @@ Any, Awaitable, Callable, + Literal, Optional, + Protocol, Union, cast, overload, + runtime_checkable, ) from .. import _utils from .._docstring import add_example -from .._typing_extensions import Literal, Protocol, runtime_checkable from . import RenderFunction, RenderFunctionAsync if TYPE_CHECKING: diff --git a/shiny/render/_render.py b/shiny/render/_render.py index 7505c31cc..7915b992c 100644 --- a/shiny/render/_render.py +++ b/shiny/render/_render.py @@ -51,9 +51,10 @@ from ..session._utils import RenderedDeps import pandas as pd +from typing import Protocol, runtime_checkable + from .. import _utils from .._namespaces import ResolvedId -from .._typing_extensions import Protocol, runtime_checkable from ..types import ImgData from ._try_render_plot import try_render_matplotlib, try_render_pil, try_render_plotnine diff --git a/shiny/types.py b/shiny/types.py index 455480152..3263a47fe 100644 --- a/shiny/types.py +++ b/shiny/types.py @@ -12,12 +12,12 @@ "SilentCancelOutputException", ) -from typing import TYPE_CHECKING, Any, BinaryIO, Optional +from typing import TYPE_CHECKING, Any, BinaryIO, Literal, Optional, Protocol from htmltools import TagChild from ._docstring import add_example -from ._typing_extensions import Literal, NotRequired, Protocol, TypedDict +from ._typing_extensions import NotRequired, TypedDict if TYPE_CHECKING: from matplotlib.figure import Figure diff --git a/shiny/ui/_bootstrap.py b/shiny/ui/_bootstrap.py index 90d1a2bc7..1cb024881 100644 --- a/shiny/ui/_bootstrap.py +++ b/shiny/ui/_bootstrap.py @@ -15,7 +15,7 @@ ) -from typing import Optional +from typing import Literal, Optional from htmltools import ( Tag, @@ -33,7 +33,6 @@ from .._deprecated import warn_deprecated from .._docstring import add_example -from .._typing_extensions import Literal from ..module import current_namespace from ..types import MISSING, MISSING_TYPE from ._html_dependencies import jqui_deps diff --git a/shiny/ui/_include_helpers.py b/shiny/ui/_include_helpers.py index c425d2fc6..98865ae36 100644 --- a/shiny/ui/_include_helpers.py +++ b/shiny/ui/_include_helpers.py @@ -8,12 +8,12 @@ import shutil import tempfile from pathlib import Path +from typing import Literal # TODO: maybe these include_*() functions should actually live in htmltools? from htmltools import HTMLDependency, Tag, TagAttrValue, tags from .._docstring import add_example -from .._typing_extensions import Literal # TODO: it's bummer that, when method="link_files" and path is in the same directory # as the app, the app's source will be included. Should we just not copy .py/.r files? diff --git a/shiny/ui/_input_file.py b/shiny/ui/_input_file.py index eea65409e..53b9ee2c7 100644 --- a/shiny/ui/_input_file.py +++ b/shiny/ui/_input_file.py @@ -2,13 +2,12 @@ __all__ = ("input_file",) -from typing import Optional +from typing import Literal, Optional from htmltools import Tag, TagChild, css, div, span, tags from .._docstring import add_example from .._namespaces import resolve_id -from .._typing_extensions import Literal from ._utils import shiny_input_label diff --git a/shiny/ui/_input_text.py b/shiny/ui/_input_text.py index 693c10983..d94ff3f9d 100644 --- a/shiny/ui/_input_text.py +++ b/shiny/ui/_input_text.py @@ -1,12 +1,11 @@ __all__ = ("input_text", "input_text_area") -from typing import Optional +from typing import Literal, Optional from htmltools import Tag, TagChild, css, div, tags from .._docstring import add_example from .._namespaces import resolve_id -from .._typing_extensions import Literal from ._utils import shiny_input_label diff --git a/shiny/ui/_input_update.py b/shiny/ui/_input_update.py index f33db12fb..ca455a776 100644 --- a/shiny/ui/_input_update.py +++ b/shiny/ui/_input_update.py @@ -21,7 +21,7 @@ import json import re from datetime import date -from typing import Mapping, Optional +from typing import Literal, Mapping, Optional from htmltools import TagChild from starlette.requests import Request @@ -29,7 +29,7 @@ from .._docstring import add_example, doc_format from .._namespaces import resolve_id -from .._typing_extensions import Literal, NotRequired, TypedDict +from .._typing_extensions import NotRequired, TypedDict from .._utils import drop_none from ..session import Session, require_active_session from ._input_check_radio import ChoicesArg, _generate_options diff --git a/shiny/ui/_insert.py b/shiny/ui/_insert.py index 28669c164..c802cd741 100644 --- a/shiny/ui/_insert.py +++ b/shiny/ui/_insert.py @@ -1,11 +1,10 @@ __all__ = ("insert_ui", "remove_ui") -from typing import Optional +from typing import Literal, Optional from htmltools import TagChild from .._docstring import add_example -from .._typing_extensions import Literal from ..session import Session, require_active_session diff --git a/shiny/ui/_markdown.py b/shiny/ui/_markdown.py index 6dcdb2b90..e8e0cf40b 100644 --- a/shiny/ui/_markdown.py +++ b/shiny/ui/_markdown.py @@ -3,12 +3,11 @@ import importlib import textwrap import warnings -from typing import Callable, Optional +from typing import Callable, Literal, Optional from htmltools import HTML from .._docstring import add_example -from .._typing_extensions import Literal @add_example() diff --git a/shiny/ui/_modal.py b/shiny/ui/_modal.py index 4b72a4a6a..a1203255c 100644 --- a/shiny/ui/_modal.py +++ b/shiny/ui/_modal.py @@ -7,12 +7,11 @@ "modal_remove", ) -from typing import Optional +from typing import Literal, Optional from htmltools import HTML, Tag, TagAttrs, TagAttrValue, TagChild, div, tags from .._docstring import add_example -from .._typing_extensions import Literal from ..session import Session, require_active_session from ..types import MISSING, MISSING_TYPE diff --git a/shiny/ui/_navs.py b/shiny/ui/_navs.py index ce5deed66..d23133c50 100644 --- a/shiny/ui/_navs.py +++ b/shiny/ui/_navs.py @@ -16,13 +16,12 @@ import copy import re -from typing import Any, Optional, Sequence, cast +from typing import Any, Literal, Optional, Sequence, cast from htmltools import MetadataNode, Tag, TagChild, TagList, div, tags from .._docstring import add_example from .._namespaces import resolve_id -from .._typing_extensions import Literal from .._utils import private_random_int from ..types import NavSetArg from ._bootstrap import column, row diff --git a/shiny/ui/_notification.py b/shiny/ui/_notification.py index 369580a8e..40d4c83f7 100644 --- a/shiny/ui/_notification.py +++ b/shiny/ui/_notification.py @@ -2,12 +2,11 @@ __all__ = ("notification_show", "notification_remove") -from typing import Any, Optional +from typing import Any, Literal, Optional from htmltools import TagChild from .._docstring import add_example -from .._typing_extensions import Literal from .._utils import rand_hex from ..session import Session, require_active_session diff --git a/shiny/ui/_page.py b/shiny/ui/_page.py index deb314f0f..af09802b8 100644 --- a/shiny/ui/_page.py +++ b/shiny/ui/_page.py @@ -7,13 +7,12 @@ "page_bootstrap", ) -from typing import Optional, Sequence +from typing import Literal, Optional, Sequence from htmltools import MetadataNode, Tag, TagAttrs, TagChild, TagList, div, tags from .._docstring import add_example from .._namespaces import resolve_id -from .._typing_extensions import Literal from ..types import MISSING, MISSING_TYPE, NavSetArg from ._html_dependencies import bootstrap_deps from ._navs import navset_bar diff --git a/shiny/ui/_plot_output_opts.py b/shiny/ui/_plot_output_opts.py index 14fe15c8e..d9c542b8b 100644 --- a/shiny/ui/_plot_output_opts.py +++ b/shiny/ui/_plot_output_opts.py @@ -1,8 +1,9 @@ from __future__ import annotations import re +from typing import Literal -from .._typing_extensions import Literal, NotRequired, TypedDict +from .._typing_extensions import NotRequired, TypedDict class ClickOpts(TypedDict): diff --git a/shiny/ui/_x/_sidebar.py b/shiny/ui/_x/_sidebar.py index 449965a81..c3a43120b 100644 --- a/shiny/ui/_x/_sidebar.py +++ b/shiny/ui/_x/_sidebar.py @@ -1,14 +1,12 @@ from __future__ import annotations import random -from typing import Optional +from typing import Literal, Optional from htmltools import Tag, TagAttrs, TagAttrValue, TagChild, TagList, css, div from htmltools import svg as svgtags from htmltools import tags -from ..._typing_extensions import Literal - # from ._color import get_color_contrast from ._css_unit import CssUnit, as_css_padding, as_css_unit from ._fill import as_fill_item, as_fillable_container