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

Drop 3.6 and 3.7 and support 3.11 and 3.12 #2735

Merged
merged 22 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2d156ab
test on 3.12
graingert-coef Jan 31, 2024
c436211
test on 3.8
graingert-coef Jan 31, 2024
66113b7
drop support for 3.6 and 3.7 add support for 3.11 and 3.12
graingert-coef Jan 31, 2024
028cfdf
replace all uses of python36 with python38 and python310 with python312
graingert-coef Jan 31, 2024
8b66645
add changelog entry
graingert-coef Jan 31, 2024
26a4c44
bump numpy, orjson, pyzmq, flake8 and pylint for 3.12 suppport
graingert-coef Jan 31, 2024
3826502
use Python 3.12 on windows
graingert-coef Jan 31, 2024
7b53486
switch to pylintrc312
graingert-coef Jan 31, 2024
bc21890
add new pylint violations to pylintrc
graingert-coef Jan 31, 2024
bc0e46e
vendor preconditions
graingert-coef Jan 31, 2024
78b0567
replace preconditions call to getargspec with getfullargspec
graingert-coef Jan 31, 2024
f1c84c4
enable some pylint rules
graingert-coef Jan 31, 2024
09ec06a
update .pylintrc config
graingert-coef Jan 31, 2024
6fcc5fb
support chrome driver.name.lower() being chrome-headless-shell
graingert-coef Jan 31, 2024
576f940
get exceptions working on Py3.11+
graingert-coef Jan 31, 2024
8d89156
prevent celery tests hanging by installing setuptools
graingert-coef Feb 6, 2024
a6486e4
remove dash.html and dash.dash_table from extension-pkg-allow-list
graingert-coef Feb 6, 2024
ff11147
replace extension-pkg-whitelist with extension-pkg-allow-list
graingert-coef Feb 6, 2024
5483d86
remove preconditions dep
graingert-coef Feb 6, 2024
a21e2d2
delete requires-all.txt it's generated in ci
graingert-coef Feb 6, 2024
1544377
Update components/dash-table/tests/selenium/conftest.py
graingert-coef Feb 8, 2024
87adad8
clean up python_version specifiers from requirements files
graingert-coef Feb 8, 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
201 changes: 103 additions & 98 deletions .circleci/config.yml

Large diffs are not rendered by default.

26 changes: 9 additions & 17 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code
extension-pkg-whitelist=
extension-pkg-allow-list=

# Add files or directories to the blacklist. They should be base names, not
# paths.
Expand Down Expand Up @@ -68,7 +68,13 @@ disable=fixme,
superfluous-parens,
bad-continuation,
line-too-long,
bad-option-value
bad-option-value,
use-dict-literal,
missing-timeout,
unnecessary-dunder-call,
unnecessary-lambda-assignment,
broad-exception-raised,
consider-using-generator,


# Enable the message, report, category or checker with the given id(s). You can
Expand Down Expand Up @@ -234,13 +240,6 @@ indent-string=' '
# Maximum number of lines in a module
max-module-lines=1000

# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=trailing-comma,
dict-separator

# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
single-line-class-stmt=no
Expand Down Expand Up @@ -415,7 +414,7 @@ max-bool-expr=5
max-branches=15

# Maximum number of locals for function / method body
max-locals=15
max-locals=18

# Maximum number of parents for a class (see R0901).
max-parents=7
Expand Down Expand Up @@ -467,10 +466,3 @@ known-standard-library=

# Force import order to recognize a module as part of a third party library.
known-third-party=enchant


[EXCEPTIONS]

# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception
21 changes: 10 additions & 11 deletions .pylintrc310 → .pylintrc312
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code.
extension-pkg-whitelist=
extension-pkg-allow-list=

# Add files or directories to the blacklist. They should be base names, not
# paths.
Expand Down Expand Up @@ -159,7 +159,13 @@ disable=invalid-name,
line-too-long,
super-with-arguments,
raise-missing-from,
bad-option-value
bad-option-value,
use-dict-literal,
missing-timeout,
unnecessary-dunder-call,
unnecessary-lambda-assignment,
broad-exception-raised,
consider-using-generator,

# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
Expand Down Expand Up @@ -332,13 +338,6 @@ indent-string=' '
# Maximum number of lines in a module.
max-module-lines=1000

# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=trailing-comma,
dict-separator

# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
single-line-class-stmt=no
Expand Down Expand Up @@ -516,7 +515,7 @@ max-bool-expr=5
max-branches=15

# Maximum number of locals for function / method body.
max-locals=15
max-locals=18

# Maximum number of parents for a class (see R0901).
max-parents=7
Expand Down Expand Up @@ -574,4 +573,4 @@ known-third-party=enchant

# Exceptions that will emit a warning when being caught. Defaults to
# "Exception".
overgeneral-exceptions=Exception
overgeneral-exceptions=builtins.Exception
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
## Changed

- [#2734](https://github.com/plotly/dash/pull/2734) Configure CI for Python 3.10 [#1863](https://github.com/plotly/dash/issues/1863)
- [#2735](https://github.com/plotly/dash/pull/2735) Configure CI for Python 3.8 and 3.12, drop support for Python 3.6 and Python 3.7 [#2736](https://github.com/plotly/dash/issues/2736)

## [2.15.0] - 2024-01-31

Expand Down
2 changes: 1 addition & 1 deletion components/dash-core-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"private::format.black": "black dash_core_components_base/ tests/ setup.py",
"private::format.eslint": "eslint src --fix",
"private::format.prettier": "prettier --config .prettierrc --write src/**/*.js",
"private::lint.black": "node -e \"if ((process.env.PYVERSION || 'python310') !== 'python36'){process.exit(1)} \" || black --check dash_core_components_base/ tests/ setup.py",
"private::lint.black": "node -e \"if ((process.env.PYVERSION || 'python312') !== 'python38'){process.exit(1)} \" || black --check dash_core_components_base/ tests/ setup.py",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose we can leave this as you have it for now, but way better would be to pick a version of black that supports all the same Python versions we do and always run it. The only reason we didn't do that from the start is that when we first introduced black we still supported Py2.7 and black was always Py3-only.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do this in a follow up PR

"private::lint.eslint": "eslint src",
"private::lint.flake8": "flake8 --exclude=dash_core_components,node_modules,venv",
"private::lint.prettier": "prettier --config .prettierrc src/**/*.js --list-different",
Expand Down
2 changes: 1 addition & 1 deletion components/dash-table/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"private::format.black": "black dash_table_base tests",
"private::lint.ts": "eslint ./src ./tests",
"private::lint.flake": "flake8 dash_table_base tests",
"private::lint.black": "node -e \"if ((process.env.PYVERSION || 'python310') !== 'python36'){process.exit(1)} \" || black --check dash_table_base tests",
"private::lint.black": "node -e \"if ((process.env.PYVERSION || 'python312') !== 'python38'){process.exit(1)} \" || black --check dash_table_base tests",
"private::lint.prettier": "prettier --config .prettierrc \"{src,tests,demo}/**/*.{js,ts,tsx}\" --list-different",
"private::test.python": "python -m unittest tests/unit/format_test.py",
"private::test.unit": "karma start karma.conf.js --single-run",
Expand Down
90 changes: 89 additions & 1 deletion components/dash-table/tests/selenium/conftest.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,102 @@
import platform
import pytest
from functools import wraps
import inspect

from dash.testing.browser import Browser
from preconditions import preconditions
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait


# @preconditions decorator, copied from the preconditions PyPI package
# https://pypi.org/project/preconditions/
# https://github.com/nejucomo/preconditions
# and modified to support Python 3.12


class PreconditionError(TypeError):
graingert-coef marked this conversation as resolved.
Show resolved Hide resolved
pass


def preconditions(*precs):
stripped_source = lambda obj: inspect.getsource(obj).strip()

if not precs:
# This edge case makes ``@preconditions()`` efficiently delegate
# to the wrapped function, which I anticipate will be useful
# for stubbing and code consistency in applications:
def null_decorator(f):
f.nopre = f # Meet the .nopre interface requirement.
return f

return null_decorator

precinfo = []
for p in precs:
spec = inspect.getfullargspec(p)
if spec.varargs or spec.varkw:
raise PreconditionError(
(
"Invalid precondition must not accept * nor ** args:\n" + " {!s}\n"
).format(stripped_source(p))
)

i = -len(spec.defaults or ())
if i == 0:
appargs, closureargs = spec.args, []
else:
appargs, closureargs = spec.args[:i], spec.args[i:]
precinfo.append((appargs, closureargs, p))

def decorate(f):
fspec = inspect.getfullargspec(f)

for (appargs, closureargs, p) in precinfo:
for apparg in appargs:
if apparg not in fspec.args:
raise PreconditionError(
(
"Invalid precondition refers to unknown parameter {!r}:\n"
+ " {!s}\n"
+ "Known parameters: {!r}\n"
).format(apparg, stripped_source(p), fspec.args)
)
for carg in closureargs:
if carg in fspec.args:
raise PreconditionError(
(
"Invalid precondition masks parameter {!r}:\n"
+ " {!s}\n"
+ "Known parameters: {!r}\n"
).format(carg, stripped_source(p), fspec.args)
)

@wraps(f)
def g(*a, **kw):
args = inspect.getcallargs(f, *a, **kw)
for (appargs, _, p) in precinfo:
if not p(*[args[aa] for aa in appargs]):
raise PreconditionError(
"Precondition failed in call {!r}{}:\n {!s}\n".format(
g,
inspect.formatargvalues(
fspec.args, fspec.varargs, fspec.varkw, args
),
stripped_source(p),
)
)

return f(*a, **kw)

g.nopre = f
return g

return decorate


_validate_col = lambda col: (isinstance(col, str) and len(col) > 0) or (
isinstance(col, int) and col >= 0
)
Expand Down
7 changes: 5 additions & 2 deletions dash/_callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
from ._callback_context import context_value


def _invoke_callback(func, *args, **kwargs): # used to mark the frame for the debugger
return func(*args, **kwargs) # %% callback invoked %%


class NoUpdate:
def to_plotly_json(self): # pylint: disable=no-self-use
return {"_dash_no_update": "_dash_no_update"}
Expand Down Expand Up @@ -438,8 +442,7 @@ def add_context(*args, **kwargs):
if output_value is callback_manager.UNDEFINED:
return to_json(response)
else:
# don't touch the comment on the next line - used by debugger
output_value = func(*func_args, **func_kwargs) # %% callback invoked %%
output_value = _invoke_callback(func, *func_args, **func_kwargs)

if NoUpdate.is_no_update(output_value):
raise PreventUpdate
Expand Down
19 changes: 12 additions & 7 deletions dash/_jupyter.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import sys
import threading
import time
import traceback

from typing_extensions import Literal

Expand Down Expand Up @@ -37,12 +36,18 @@


def _get_skip(error: Exception):
tb = traceback.format_exception(type(error), error, error.__traceback__)
skip = 0
for i, line in enumerate(tb):
if "%% callback invoked %%" in line:
skip = i + 1
break
from dash._callback import ( # pylint: disable=import-outside-toplevel
_invoke_callback,
)

tb = error.__traceback__
skip = 1
while tb.tb_next is not None:
skip += 1
tb = tb.tb_next
if tb.tb_frame.f_code is _invoke_callback.__code__:
return skip

return skip


Expand Down
48 changes: 30 additions & 18 deletions dash/dash.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,32 +146,44 @@ def _get_traceback(secret, error: Exception):
except ImportError:
tbtools = None

def _get_skip(text, divider=2):
skip = 0
for i, line in enumerate(text):
if "%% callback invoked %%" in line:
skip = int((i + 1) / divider)
break
def _get_skip(error):
from dash._callback import ( # pylint: disable=import-outside-toplevel
_invoke_callback,
)

tb = error.__traceback__
skip = 1
while tb.tb_next is not None:
skip += 1
tb = tb.tb_next
if tb.tb_frame.f_code is _invoke_callback.__code__:
return skip

return skip

def _do_skip(error):
from dash._callback import ( # pylint: disable=import-outside-toplevel
_invoke_callback,
)

tb = error.__traceback__
while tb.tb_next is not None:
if tb.tb_frame.f_code is _invoke_callback.__code__:
return tb.tb_next
tb = tb.tb_next
return error.__traceback__

# werkzeug<2.1.0
if hasattr(tbtools, "get_current_traceback"):
tb = tbtools.get_current_traceback()
skip = _get_skip(tb.plaintext.splitlines())
return tbtools.get_current_traceback(skip=skip).render_full()
return tbtools.get_current_traceback(skip=_get_skip(error)).render_full()

if hasattr(tbtools, "DebugTraceback"):
tb = tbtools.DebugTraceback(error) # pylint: disable=no-member
skip = _get_skip(tb.render_traceback_text().splitlines())

# pylint: disable=no-member
return tbtools.DebugTraceback(error, skip=skip).render_debugger_html(
True, secret, True
)
return tbtools.DebugTraceback(
error, skip=_get_skip(error)
).render_debugger_html(True, secret, True)

tb = traceback.format_exception(type(error), error, error.__traceback__)
skip = _get_skip(tb, 1)
return tb[0] + "".join(tb[skip:])
return "".join(traceback.format_exception(type(error), error, _do_skip(error)))


# Singleton signal to not update an output, alternative to PreventUpdate
Expand Down
4 changes: 2 additions & 2 deletions dash/testing/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ def get_logs(self):

Chrome only
"""
if self.driver.name.lower() == "chrome":
if self._browser == "chrome":
return [
entry
for entry in self.driver.get_log("browser")
Expand All @@ -614,7 +614,7 @@ def get_logs(self):

def reset_log_timestamp(self):
"""reset_log_timestamp only work with chrome webdriver."""
if self.driver.name.lower() == "chrome":
if self._browser == "chrome":
entries = self.driver.get_log("browser")
if entries:
self._last_ts = entries[-1]["timestamp"]
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
"private::cibuild.renderer": "cd dash/dash-renderer && renderer build",
"private::build.renderer": "cd dash/dash-renderer && renderer build",
"private::build.jupyterlab": "cd \\@plotly/dash-jupyterlab && jlpm install && jlpm build:pack",
"private::lint.black": "node -e \"if ((process.env.PYVERSION || 'python310') !== 'python36'){process.exit(1)} \" || black dash tests --exclude metadata_test.py --check",
"private::lint.black": "node -e \"if ((process.env.PYVERSION || 'python312') !== 'python38'){process.exit(1)} \" || black dash tests --exclude metadata_test.py --check",
"private::lint.flake8": "flake8 --exclude=metadata_test.py dash tests",
"private::lint.pylint-dash": "PYLINTRC=${PYLINTRC:=.pylintrc39} && pylint dash setup.py --rcfile=$PYLINTRC",
"private::lint.pylint-tests": "PYLINTRC=${PYLINTRC:=.pylintrc39} && pylint tests/unit tests/integration -d all --rcfile=$PYLINTRC",
"private::lint.pylint-dash": "PYLINTRC=${PYLINTRC:=.pylintrc312} && pylint dash setup.py --rcfile=$PYLINTRC",
"private::lint.pylint-tests": "PYLINTRC=${PYLINTRC:=.pylintrc312} && pylint tests/unit tests/integration -d all -e C0410,C0413,W0109 --rcfile=$PYLINTRC",
"private::lint.renderer": "cd dash/dash-renderer && npm run lint",
"private::test.setup-components": "cd \\@plotly/dash-test-components && npm ci && npm run build",
"private::test.setup-nested": "cd \\@plotly/dash-generator-test-component-nested && npm ci && npm run build",
Expand Down
Loading