-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Occasional AST constructor depth mismatch #10874
Comments
As is, this looks like a nightmare to debug I believe as a starting point we ought to catch the error and raise a new one with more context |
Happy to help move it closer to a neutral dream state if there's anything on the ibis side we can do or information you need to help debug it. |
We're starting to see this failure much more often now (https://github.com/ibis-project/ibis/actions/runs/5602258994/jobs/10247421549). |
One thing I can think of to reproduce the problem is to use a pytest fork which uses a try/except block around the
When that raises a |
actually no fork needed, just ensure the conftest monkeypatches ast.parse with a wrapper that helps debug |
Findings:
|
I was able to trigger the exception and get the source that was failing to compile. Some notes:
Sourcefrom __future__ import annotations
import functools
import weakref
from collections import Counter, defaultdict
from typing import TYPE_CHECKING, Any, Callable, MutableMapping
from bidict import bidict
from ibis.common.collections import FrozenDict
from ibis.common.exceptions import IbisError
if TYPE_CHECKING:
from collections.abc import Iterator
def memoize(func: Callable) -> Callable:
"""Memoize a function."""
cache = {}
@functools.wraps(func)
def wrapper(*args, **kwargs):
key = (args, FrozenDict(kwargs))
try:
return cache[key]
except KeyError:
result = func(*args, **kwargs)
cache[key] = result
return result
return wrapper
class WeakCache(MutableMapping):
__slots__ = ('_data',)
def __init__(self):
object.__setattr__(self, '_data', {})
def __setattr__(self, name, value):
raise TypeError(f"can't set {name}")
def __len__(self) -> int:
return len(self._data)
def __iter__(self) -> Iterator[Any]:
return iter(self._data)
def __setitem__(self, key, value) -> None:
# construct an alternative representation of the key using the id()
# of the key's components, this prevents infinite recursions
identifiers = tuple(id(item) for item in key)
# create a function which removes the key from the cache
def callback(ref_):
return self._data.pop(identifiers, None)
# create weak references for the key's components with the callback
# to remove the cache entry if any of the key's components gets
# garbage collected
refs = tuple(weakref.ref(item, callback) for item in key)
self._data[identifiers] = (value, refs)
def __getitem__(self, key):
identifiers = tuple(id(item) for item in key)
value, _ = self._data[identifiers]
return value
def __delitem__(self, key):
identifiers = tuple(id(item) for item in key)
del self._data[identifiers]
def __repr__(self):
return f"{self.__class__.__name__}({self._data})"
class RefCountedCache:
"""A cache with reference-counted keys.
We could implement `MutableMapping`, but the `__setitem__` implementation
doesn't make sense and the `len` and `__iter__` methods aren't used.
We can implement that interface if and when we need to.
"""
def __init__(
self,
*,
populate: Callable[[str, Any], None],
lookup: Callable[[str], Any],
finalize: Callable[[Any], None],
generate_name: Callable[[], str],
key: Callable[[Any], Any],
) -> None:
self.cache = bidict()
self.refs = Counter()
self.populate = populate
self.lookup = lookup
self.finalize = finalize
self.names = defaultdict(generate_name)
self.key = key or (lambda x: x)
def get(self, key, default=None):
try:
return self[key]
except KeyError:
return default
def __getitem__(self, key):
result = self.cache[key]
self.refs[key] += 1
return result
def store(self, input) -> None:
"""Compute and store a reference to `key`."""
key = self.key(input)
name = self.names[key]
self.populate(name, input)
self.cache[key] = self.lookup(name)
# nothing outside of this instance has referenced this key yet, so the
# refcount is zero
#
# in theory it's possible to call store -> delitem which would raise an
# exception, but in practice this doesn't happen because the only call
# to store is immediately followed by a call to getitem.
self.refs[key] = 0
def __delitem__(self, key) -> None:
# we need to remove the expression representing the computed physical
# table as well as the expression that was used to create that table
#
# bidict automatically handles this for us; without it we'd have to do
# to the bookkeeping ourselves with two dicts
if (inv_key := self.cache.inverse.get(key)) is None:
raise IbisError(
"Key has already been released. Did you call "
"`.release()` twice on the same expression?"
)
self.refs[inv_key] -= 1
assert self.refs[inv_key] >= 0, f"refcount is negative: {self.refs[inv_key]:d}"
if not self.refs[inv_key]:
del self.cache[inv_key], self.refs[inv_key]
self.finalize(key) |
Thanks @cpcloud! Seems like this is an upstream issue fixed in python/cpython#95208, should we close this then? |
Hm, isn't that the change that introduced the problem I'm seeing? In particular these lines: https://github.com/python/cpython/pull/95208/files#diff-c4dd13901c011c278fe5b6d175fdd23ed94e51568fe6b2ee4215d5ab376be9a8R1403-R1405 |
Ahh my bad, I did not read things thoroughly and thought this was fixed by that PR. Hmm bummer, I cannot reproduce this on Windows (Python 3.11.3): import ast
from pathlib import Path
content = Path("problem.py").read_text()
astnode = ast.parse(content, "source", "exec") (With Can you see if you can reproduce the problem @cpcloud? |
I tried reproducing it by creating a bunch of threads as well, no luck. |
I don't think this has anything to do with the particular source code that's being parsed. I can reproduce this (but only when pytest is in the mix) and there are different files for which the problem is observed. |
@nicoddemus Any chance I can help you get set up enough to reproduce this locally? I can only reproduce the issue when running pytest with our impala backend. We have spent a ton of time making this kind of setup relatively easy to get started with. I'd even be happy to pair with you on it! |
Hi @cpcloud, I appreciate the offer but TBH I don't think I can spare the time to work on this, sorry. 😕 |
No worries, I understand! |
…ython 3.11.x - More information: pytest-dev/pytest#10874
- Temporary fix for AST constructor depth mismatch in pytest on python 3.11.x, More information: pytest-dev/pytest#10874
- Temporary fix for AST constructor depth mismatch in pytest on python 3.11.x, More information: pytest-dev/pytest#10874
- Temporary fix for AST constructor depth mismatch in pytest on python 3.11.x, More information: pytest-dev/pytest#10874
- Temporary fix for AST constructor depth mismatch in pytest on python 3.11.x, More information: pytest-dev/pytest#10874
…ixed #369) [#370] 🚧 Setup.py: - Removed support for python-3.7 legacies - 📌 Raised `python_requires` to `>=3.8`. Thereby python `3.7` and any before legacy are no longer supported. - 🔥 Removed `3.7` legacy from Programming Language metadata. - 🩹 Readded latest patch to `uvicorn`, `starlette`, `pyzmq` dependencies. - 📌 Pinned pyzmq==24.0.1. Asyncio: - 🩹 Formatted TemplateResponse class parameters w.r.t new changes. CI: - 👷 Updated Azure Pipeline workflow. - ✨ Added python 3.11 legacy support for MacOS environments. - 🔥 Deprecated python 3.7 legacy support. - 👷 Updated Appveyor Pipeline workflow. - ✨ Added python 3.11 legacy support for Windows OS environments. - 🔥 Deprecated python 3.7 legacy support. -👷 Updated GitHub CI Pipeline workflow. - ✨ Added python 3.11 legacy support for Linux OS environments. - 🔥 Deprecated python 3.7 legacy support. - 🚚 Migrated python version to 3.9 in deploy_docs.yml workflow. - 💚 Temporary fix for AST constructor depth mismatch in pytest on python `3.11.x`, More information: pytest-dev/pytest#10874 - Made temporary fix platform independent. - Extended fix to all Webgear_RTC tests. - ☂️ Increased code coverage. - 💚 Fixed condition logic bug. Maintenance: - ⚡️ Added GitHub sponsors and dropped liberapay from Funding.yml. Docs: - 📝 Updated minimum python to version `3.8` while installing vidgear. - 🎨 Updated API-specific dependencies. - ✏️ Fixed hyperlinks.
### New Features ✨ - **NetGear:** * Added new `kill` parameter to `close()` method to forcefully kill ZMQ context instead of graceful exit only in the `receive` mode. * Added new `subscriber_timeout` integer optional parameter to support timeout with `pattern=2` _(or Publisher-Subscriber)_ pattern. + Receiver will exit safely if timeout defined(any value(in milliseconds) > 0), and timeout occurs in Receiver Mode with `pattern=2`. + 💬 Note: Default behavior still is to block the thread till infinite time. - **WriteGear:** * Added new `-disable_ffmpeg_window` optional Boolean flag to enable patch that prevents FFmpeg creation window from opening when building `.exe` files on Windows OS. _(PR by @ibtsam3301)_ + 💬 Note: `-disable_ffmpeg_window` optional Boolean flag is only available on Windows OS with logging disabled(`logging=False`) in compression mode. + Use Case: This flag can be useful while creating an `.exe` file for a python script that uses WriteGear API. On windows even after creating the `.exe` file in windowed mode or no-console mode, the `ffmpeg.exe` command line window would pop up while its being used by WriteGear API. - **Setup.py** * Added official support for python `3.11.x` legacies. * Bumped version to `0.3.1`. - **Docs** * Added doc for `subscriber_timeout` optional Integer parameter in NetGear. * Added doc for `disable_ffmpeg_window` optional Boolean parameter in WriteGear. * Added new asset `screengear_region.png`. - **CI** * Added python 3.11 legacy support for MacOS, Windows and Linux environments. * Added kill argument to close() method in various NetGear tests. ### Updates/Improvements ⚡️ - Asyncio: * Formatted TemplateResponse class parameters w.r.t new changes in backend Starlette API. - Setup.py: * Readded latest patch to `uvicorn`, `starlette`, `pyzmq` dependencies. * Removed `3.7` legacy from Programming Language metadata. - Maintenance: * Added GitHub sponsors and dropped liberapay from `Funding.yml`. * Removed redundant code. - Docs: * Updated information related to Supported Dimensional Attributes in ScreenGear docs. * Updated minimum python to version `3.8` while installing vidgear in docs. * Updated API-specific dependencies in docs. * Updated changelog.md - CI: * Updated Azure Pipeline workflow. * Updated Appveyor Pipeline workflow. * Updated GitHub Actions Pipeline workflow. * Migrated python version to `3.9` in `deploy_docs.yml` workflow. * Removed deprecated python `3.7` legacy support. * Increased code coverage by updating tests. * Updated tests for `subscriber_timeout` optional Integer parameter in NetGear. * Updated tests for `disable_ffmpeg_window` optional Boolean parameter in WriteGear. ### Breaking Updates/Changes 💥 - [ ] Setup.py: * Removed support for python-3.7 legacies + Raised `python_requires` to `>=3.8`. Thereby python `3.7` and any before legacy are no longer supported. ### Bug-fixes 🐛 - ScreenGear: * Fixed swapped region dimensions bug with dxcam backend. * Fixed "mss" backend disabled when `monitor` parameter is not defined. - Docs: * Fixed missing `compression_mode` flags in WriteGear API docs. * Fixed missing hyperlinks. * Fixed typos and context. - CI: * Temporary fix for AST constructor depth mismatch in pytest on python `3.11.x`, More information: pytest-dev/pytest#10874 + Made temporary fix platform independent. + Extended fix to all Webgear_RTC tests. * Fixed NetGear tests bugs. * Fixed condition logic bug.
Seems like python/cpython#113035 should fix it which means the next Python point releases - 3.11.8, 3.12.2, 3.13. If this still happens with these releases, we can reopen. |
In some of our CI runs in the ibis project we occasionally see failures around AST depths mismmatching.
It's hard for me to reproduce locally.
output ofpip list
from the virtual environment you are usingThe number of transitive dependencies for ibis is large. I'm happy to list them here, but it may require a bit of discussion/help to get someone setup to reproduce this
pytest: 7.2.2
os: linux 6.1.2 (NixOS)
[ ] minimal example if possibleI don't know exactly what is minimal here, I'm having trouble reproducing it locally.
The text was updated successfully, but these errors were encountered: