You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Surprising choices_provider behavior in 4.0 when using sqlite3 #1680
People upgrading single-threaded applications to 4.0 may be surprised. Reproduce with the following methodology (tested with Python3.14.5 on OSX):
Start the shell.
Type t and then TABTAB and a SQLite error occurs.
Failed query: SQLite objects created in a thread can only be used in that same thread. The object was created in thread id 8421351040 and this is thread id 6186856448.
@bknotwell Thank you very much for bringing this behavior to our attention!
This surprising behavior is happening because in version 4.0.0, cmd2 switched to using prompt-toolkit under the hood for providing completions (instead of readline). prompt-toolkit executes tab completions , including your choices_provider, in a background thread to prevent blocking the interactive CLI. By default, Python's sqlite3 module restricts database connection objects from being accessed in any thread other than the one where they were created (check_same_thread=True).
Ways I can think of to fix this include:
Disable check_same_thread in your application - this is the simplest option and should work in most cases
Because the underlying C implementation of SQLite is compiled with thread safety enabled in Python 3, it is generally safe to share the connection across threads as long as you aren't running complex concurrent write transactions. You can disable the Python-level thread check by adding check_same_thread=False.
classFirstApp(Cmd):
# Change the connection instantiation to:conn=sqlite3.connect(":memory:", check_same_thread=False)
Use a Shared-Cache URI & Thread-Local Connections - more complex but should work in all cases
If you want to strictly maintain the check_same_thread protection while using an in-memory database, you can use SQLite's shared cache. This allows you to create a new connection inside the background thread that points to the exact same in-memory database created by the main thread.
classFirstApp(Cmd):
# 1. Update the main connection to use a shared memory URI:conn=sqlite3.connect("file:memdb1?mode=memory&cache=shared", uri=True)
# ... setup tables ...def_t_completer(self):
try:
# 2. Create a new, thread-local connection inside the completer:withsqlite3.connect("file:memdb1?mode=memory&cache=shared", uri=True) aslocal_conn:
ids= [x[0] forxinlocal_conn.execute("SELECT X FROM xxx").fetchall()]
returnChoices.from_values(ids)
exceptsqlite3.Errorasexc:
self.perror(f"Failed query: {exc}")
Provide an option to alter cmd2's threaded completion behavior by setting complete_in_thread to False - not recommended cmd2's threaded completion behavior is hardcoded to True during its PromptSession initialization to ensure the UI doesn't stutter on slow autocomplete queries, so modifying the application's data-access layer as shown in the two options above is likely the better option. If for some reason neither of those seem acceptable, we could provide an option within cmd2 to allow an application author to bypass the default behavior and perform completion on the main thread.
@kmvanbrunt Do you have any further thoughts or suggestions?
changed the title [-]Surprising choices_provider behavior in 4.0[/-][+]Surprising choices_provider behavior in 4.0 when using sqlite3[/+]on Jun 7, 2026
Imagine I have a Cmd2 shell that has a "sub-mode" that instantiates a new Cmd2 shell which means I have four test scenarios. Two of the scenarios are mixed ((Y, N) (N,Y)):
----------------------------------------
*multi-threaded(default)* *in-thread*
----------------------------------------
Y Y
Y N
N Y
N N
----------------------------------------
Apologies for the confusing labels on the table. They should've been labeled main shell and sub shell with Y|N representing multi-threaded and in-thread respectively.
cmd2 isn't designed to support nested sub-applications like that. I'm not saying that it can't be done and we haven't done anything intentionally to preclude it. However, this goes strongly against the design principles on which this framework is based.
cmd2 supports Modular Commands via CommandSets. We also support the ability for dynamically enabling and disabling CommandSets (which can include sub-commands). On top of this we support Command Categories and the ability to enable and disable these. With these mechanisms you can effectively implement applications with "modes" without relying on nested cmd2.Cmd applications.
We aren't going to tell you how to use cmd2 - if you are using it with "sub-modes" and its working for you, then great. But this is never something we intentionally set out to support and if it is working by "accident", we don't promise to not break it in the future.
Our basic philosophy is that we want cmd2 applications to have a flat command hierarchy - i.e. you don't need to be in a certain "mode" to have access to any available command - they are all available from the top level.
That being said, we absolutely do want to support stateful applications where some commands/sub-commands are available or unavailable based on various state variables.
Activity
tleonhardt commentedon Jun 7, 2026
@bknotwell Thank you very much for bringing this behavior to our attention!
This surprising behavior is happening because in version 4.0.0,
cmd2switched to usingprompt-toolkitunder the hood for providing completions (instead ofreadline).prompt-toolkitexecutes tab completions , including yourchoices_provider, in a background thread to prevent blocking the interactive CLI. By default, Python'ssqlite3module restricts database connection objects from being accessed in any thread other than the one where they were created (check_same_thread=True).Ways I can think of to fix this include:
check_same_threadin your application - this is the simplest option and should work in most casesBecause the underlying C implementation of SQLite is compiled with thread safety enabled in Python 3, it is generally safe to share the connection across threads as long as you aren't running complex concurrent write transactions. You can disable the Python-level thread check by adding
check_same_thread=False.If you want to strictly maintain the
check_same_threadprotection while using an in-memory database, you can use SQLite's shared cache. This allows you to create a new connection inside the background thread that points to the exact same in-memory database created by the main thread.complete_in_threadtoFalse- not recommendedcmd2's threaded completion behavior is hardcoded toTrueduring itsPromptSessioninitialization to ensure the UI doesn't stutter on slow autocomplete queries, so modifying the application's data-access layer as shown in the two options above is likely the better option. If for some reason neither of those seem acceptable, we could provide an option withincmd2to allow an application author to bypass the default behavior and perform completion on the main thread.@kmvanbrunt Do you have any further thoughts or suggestions?
[-]Surprising choices_provider behavior in 4.0[/-][+]Surprising choices_provider behavior in 4.0 when using sqlite3[/+]tleonhardt commentedon Jun 9, 2026
@bknotwell Let us know what you think regarding the options discussed.
If you want, I think we would be happy to make threaded completion optional within
cmd2.bknotwell commentedon Jun 9, 2026
I'd allow the user to set complete_in_thread to False when they create a shell object and keep the current multi-threaded mode as the default.
bknotwell commentedon Jun 9, 2026
We should also ensure that a "mixed-mode"--recursive Cmd2 objects should coexist if their complete_in_thread settings are different.
tleonhardt commentedon Jun 9, 2026
@bknotwell Can you please elaborate on what you mean by this? I don't follow.
bknotwell commentedon Jun 9, 2026
Imagine I have a Cmd2 shell that has a "sub-mode" that instantiates a new Cmd2 shell which means I have four test scenarios. Two of the scenarios are mixed ((Y, N) (N,Y)):
bknotwell commentedon Jun 9, 2026
Apologies for the confusing labels on the table. They should've been labeled main shell and sub shell with Y|N representing multi-threaded and in-thread respectively.
tleonhardt commentedon Jun 9, 2026
cmd2isn't designed to support nested sub-applications like that. I'm not saying that it can't be done and we haven't done anything intentionally to preclude it. However, this goes strongly against the design principles on which this framework is based.cmd2supports Modular Commands via CommandSets. We also support the ability for dynamically enabling and disabling CommandSets (which can include sub-commands). On top of this we support Command Categories and the ability to enable and disable these. With these mechanisms you can effectively implement applications with "modes" without relying on nestedcmd2.Cmdapplications.We aren't going to tell you how to use
cmd2- if you are using it with "sub-modes" and its working for you, then great. But this is never something we intentionally set out to support and if it is working by "accident", we don't promise to not break it in the future.bknotwell commentedon Jun 9, 2026
Interesting; I'll look into CommandSets.
BTW: in my experience (limited to a single-app that had 3 different modes), a mainloop called from a mainloop works as expected.
tleonhardt commentedon Jun 9, 2026
Our basic philosophy is that we want cmd2 applications to have a flat command hierarchy - i.e. you don't need to be in a certain "mode" to have access to any available command - they are all available from the top level.
That being said, we absolutely do want to support stateful applications where some commands/sub-commands are available or unavailable based on various state variables.