Fix tab completion - #2055
Conversation
| dev = Device(0) | ||
| dev.set_current() | ||
| mr = DeviceMemoryResource(dev) | ||
| assert not mr.is_ipc_enabled, "test setup: mr should not be IPC-enabled" |
There was a problem hiding this comment.
This is only testing one specific class that we know broke things. If we accept this PR, we should probably add testing that /all/ of our Cython classes autocomplete correctly.
This comment has been minimized.
This comment has been minimized.
|
Test failures indicate something wrong in the design of the test -- not a problem with the monkey-patch itself. |
| if not (hasattr(sys, "ps1") or sys.flags.inspect): | ||
| # Plain `python script.py`, `python -c ...`, pytest, etc. | ||
| return |
There was a problem hiding this comment.
This doesn't seem quite correct, where if I just run python from a shell it drops me to a REPL that I should be able to get autocompletion with but sys.flags.inspect is 0.
There was a problem hiding this comment.
Yes, but ps1 is there, right. It WFM with python.
| # This works by overriding the `property` built-in with a custom subclass of | ||
| # property, but only in the rlcompleter module. This subclass overrides the | ||
| # `__instancecheck__` method to also return True for getset_descriptor and | ||
| # member_descriptor types, which are what Cython uses for properties on cdef | ||
| # classes. |
There was a problem hiding this comment.
Are we concerned about any implications of other modules Cython classes getset_descriptor objects?
There was a problem hiding this comment.
No. If they wanted them to be private, raising an exception unconditionally would not be the way.
| class _PatchedProperty(metaclass=_PatchedPropMeta): | ||
| pass | ||
|
|
||
| rlcompleter.property = _PatchedProperty |
There was a problem hiding this comment.
Note for posterity: This patches the built-in property but only within this module.
Co-authored-by: Leo Fang <leof@nvidia.com>
|
This still had a test to confirm things aren't fixed when the patch isn't installed. Since we always install the patch now (except when the envvar is set), that test was failing. Removed the test -- hopefully things pass now. |
|
Admin-merging this to unblock the release... There seems to exist a job scheduling problem that the L4 job refuses to run. |
This comment has been minimized.
This comment has been minimized.
1 similar comment
|
* Fix tab completion * Fix tests * Always install the monkeypatch * Update release note * Apply suggestion from @leofang Co-authored-by: Leo Fang <leof@nvidia.com> * Fix test * Fix tests hanging on Windows --------- Co-authored-by: Leo Fang <leof@nvidia.com>
`cuda/core/__init__.py` reads `CUDA_CORE_DONT_FIX_TAB_COMPLETION` with a
bare `int(os.environ.get(..., "0"))` at import time. `int()` raises for any
value that is not a base-10 integer, and `os.environ.get` returns the empty
string (not the `"0"` default) when the variable is set but empty, so:
export CUDA_CORE_DONT_FIX_TAB_COMPLETION=
python -c "import cuda.core"
ValueError: invalid literal for int() with base 10: ''
Clearing a variable with `export VAR=` is the usual way to neutralize it in
a shell profile, a Dockerfile, or a CI job spec, and `=true` / `=yes` are
the obvious guesses for a boolean-looking opt-out. All of them make the
whole package unimportable, which is a hard failure for a knob whose only
purpose is to skip an optional `rlcompleter` patch.
Parse the value leniently instead. Integer values keep their existing
meaning (non-zero opts out, so `0` and `00` still install the patch), while
a non-integer, non-empty value is honored as an opt-out rather than being
silently ignored. Unset and empty/whitespace-only both mean "not set".
Also document the variable, which was not listed on the environment
variables page, and drop the stale "only installed in interactive mode"
comment: the interactivity gate was intentionally removed in NVIDIA#2055 ("Always
install the monkeypatch"), so the patch has been unconditional since then.
The new parametrized test asserts the resulting behavior for eight values;
four of them ("", " ", "true", "yes") fail on main because the subprocess
exits non-zero with the ValueError above.
…lue (#2535) `cuda/core/__init__.py` reads `CUDA_CORE_DONT_FIX_TAB_COMPLETION` with a bare `int(os.environ.get(..., "0"))` at import time. `int()` raises for any value that is not a base-10 integer, and `os.environ.get` returns the empty string (not the `"0"` default) when the variable is set but empty, so: export CUDA_CORE_DONT_FIX_TAB_COMPLETION= python -c "import cuda.core" ValueError: invalid literal for int() with base 10: '' Clearing a variable with `export VAR=` is the usual way to neutralize it in a shell profile, a Dockerfile, or a CI job spec, and `=true` / `=yes` are the obvious guesses for a boolean-looking opt-out. All of them make the whole package unimportable, which is a hard failure for a knob whose only purpose is to skip an optional `rlcompleter` patch. Parse the value leniently instead. Integer values keep their existing meaning (non-zero opts out, so `0` and `00` still install the patch), while a non-integer, non-empty value is honored as an opt-out rather than being silently ignored. Unset and empty/whitespace-only both mean "not set". Also document the variable, which was not listed on the environment variables page, and drop the stale "only installed in interactive mode" comment: the interactivity gate was intentionally removed in #2055 ("Always install the monkeypatch"), so the patch has been unconditional since then. The new parametrized test asserts the resulting behavior for eight values; four of them ("", " ", "true", "yes") fail on main because the subprocess exits non-zero with the ValueError above. Co-authored-by: Michael Droettboom <mdboom@gmail.com>
This is a possible fix for #2053.
I almost never reach for monkey-patching as a solution, however in this case:
The alternatives are way worse and intrusive and force us into API design corners for the niche use case of tab completion (see my comments about the agent's other suggestions).
The monkeypatch is quite narrow -- it doesn't patch builtins that might have broad implications. It should only affect autocompletion with
readline. A project like that might exist, but it's probably pretty niche. IPython and Jupyter are not affected by the bug to begin with, and the patch has no effect on them. (The patch is /installed/ with IPython, but then the code never gets called. I experimented with detecting IPython and not installing the patch, but it was complex, specific to IPython, and ultimately doesn't matter).This monkeypatch is idempotent. If it gets cargo-culted to multiple projects, and those projects get imported in the same process, it will still work and be effective.
(Pending the CI run passing) It should be fine across all versions of Python we support, and there is a test here to confirm that going forward. If Python "fixes" the issue such that this patch is no longer needed, the test here should catch that and we can gate it at that point (as part of the usual "bring up on a new Python version" process).