Python REPL tab completion on MacOS - python

Before upgrading to lion, I had tab complete working in a python shell via terminal. Following these instructions, it was possible to have tab complete working.
Since upgrading to Lion, I am now unable to get tab complete working in a terminal session of Python. I've followed the above instructions to the letter, and it still does not work.
Is there a difference with the readline module in Lion? Hooking in to the 'tab:complete' option no longer seems to work. I'm wondering if it is terminal that is ignoring readline, or if it is python itself.
Python version: 2.7.1
Edit:
By tab complete, I mean I could do something like the following:
# django
import MyModel
MyModel.objects.a[TAB] # will complete to all()

Apple does not ship GNU readline with OS X. It does ship BSD libedit which includes a readline compatibility interface. The system Pythons shipped by Apple and the 64-bit/32-bit Pythons from python.org installers are built with libedit. The problem is that the commands supported by libedit are completely different from those of readline (see for example the discussion here). The traditional 32-bit-only python.org installers do use GNU readline as do some other 3rd-party distributors of Python for OS X, like MacPorts. Chances are that you were previously using such a Python and not a recent Apple one. You do have a few options, besides modifying Django: you can install the third-party replacement readline module; or you can use another Python that comes with GNU readline. However, you should not use the python.org 32-bit-only Pythons on 10.7 because, unfortunately, Xcode 4 on 10.7 no longer includes gcc-4.0 and the OS X 10.4u SDK which those Pythons need to build and install packages with C extension modules.
Putting the following in the python startup file will enable tab completion for both the libedit interface and the typical readline module. For more information on the python startup file, see here
import readline
import rlcompleter
if 'libedit' in readline.__doc__:
readline.parse_and_bind("bind ^I rl_complete")
else:
readline.parse_and_bind("tab: complete")

As it uses libedit/editline, the syntax to enable autocompletion is a little bit different.
You can first force emacs bindings (as it is with readline if I'm not wrong) by typing :
readline.parse_and_bind("bind -e")
Then you can add autocompletion linked to your TAB button (man editrc) :
readline.parse_and_bind("bind '\t' rl_complete")
And if you want to support indenting and has a history (found on internet), it should look like that (unless I made a mistake) :
import readline,rlcompleter
### Indenting
class TabCompleter(rlcompleter.Completer):
"""Completer that supports indenting"""
def complete(self, text, state):
if not text:
return (' ', None)[state]
else:
return rlcompleter.Completer.complete(self, text, state)
readline.set_completer(TabCompleter().complete)
### Add autocompletion
if 'libedit' in readline.__doc__:
readline.parse_and_bind("bind -e")
readline.parse_and_bind("bind '\t' rl_complete")
else:
readline.parse_and_bind("tab: complete")
### Add history
import os
histfile = os.path.join(os.environ["HOME"], ".pyhist")
try:
readline.read_history_file(histfile)
except IOError:
pass
import atexit
atexit.register(readline.write_history_file, histfile)
del histfile

Related

AttributeError: module 'curses' has no attribute 'A_ITALIC'

For some reason, I cannot for the life of me figure out why curses won't let me use the A_ITALIC attribute.
All of the other attributes seem to work with no issues, but italic specifically triggers an error message.
I'm running Python 3.10.4, here's some sample code:
import curses
def main(stdscr):
stdscr.addstr(0, 0, "This should be italicized!", curses.A_ITALIC)
stdscr.getch()
if __name__ == '__main__':
curses.wrapper(main)
Python on MacOS could support A_ITALIC if it is built using a recent version of ncurses (since 2013, e.g., ncurses 6.x). The system version of ncurses for MacOS is far too old for that.
The same applies to Python on other platforms, e.g., NetBSD and legacy Unix systems.
With correspondingly recent versions of Python, you can check which version of ncurses using the version or the ncurses_version function. However, even with a recent ncurses version, there's always the possibility of a bug in Python which interferes with providing that feature.
Python's curses wrapper (see source) knows about the feature because its name is compiled-in when Python is built:
/* ncurses extension */
#ifdef A_ITALIC
SetDictInt("A_ITALIC", A_ITALIC);
#endif
Changing the underlying ncurses would not affect that (unless Python is rebuilt).
From the curses documentation:
The exact constants available are system dependent.
I tried it in Python 3.9.12 on macOS, and it didn't work, but it did work using Python 3.10.4 in a Docker container.

pylint 1.4 reports E1101(no-member) on all C extensions

We've been long-time fans of pylint. Its static analysis has become a critical part of all our python projects and has saved tons of time chasing obscure bugs. But after upgrading from 1.3 -> 1.4, almost all compiled c extensions result in E1101(no-member) errors.
Projects that previously run perfectly clean through pylint 1.3 now complain about almost every C extension member with E1101. We've been forced to disable E1101 errors, but this materially detracts from the usefulness of pylint.
For example, this perfectly valid use of the lxml package
r"""valid.py: demonstrate pylint 1.4 error"""
from lxml import etree
print etree.Element('mydoc')
Run this through pylint, and it reports:
$ pylint -rn valid.py
No config file found, using default configuration
************* Module valid
E: 3, 6: Module 'lxml.etree' has no 'Element' member (no-member)
But it is perfectly valid:
$ python valid.py
<Element mydoc at 7fddf67b1ba8>
Here's where it gets really weird. A very small handful of C extensions seem to work just fine through pylint, e.g.:
r"""valid2.py: this one works fine"""
import sqlite3
print sqlite3.version
$ pylint -rn valid2.py
No config file found, using default configuration
My question is, has anyone else witnessed this? And if so, would you be willing to share your workaround/solution?
We've experimented with trying to create plugins to suppress these warnings
(http://docs.pylint.org/plugins.html#enter-plugin), but we're having difficulty making heads or tails of the docs -- and the astroid base class is uber-complex and has defied our attempts to grok it.
For real bonus points (and our eternal gratitude) we'd love to understand what changed in pylint. We'd be happy to fix the code (or at least publish a best practice document for C extension authors) that would satisfy pylint.
Platform details
$ pylint --version
No config file found, using default configuration
pylint 1.4.0,
astroid 1.3.2, common 0.63.2
Python 2.7.5 (default, Jul 1 2013, 18:09:11)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)]
Shortly after posting my question, I found the answer. The change was in fact done on purpose as a security measure. Pylint imports modules to effectively identify valid methods and attributes. It was decided that importing c extensions that are not part of the python stdlib is a security risk and could introduce malicious code.
This was done in the release of Astroid 1.3.1 https://mail.python.org/pipermail/code-quality/2014-November/000394.html
Only C extensions from trusted sources (the standard library) are
loaded into the examining Python process to build an AST from the live
module.
There are four solutions if you want to use pylint on projects that import non-stdlib c extensions.
1) Disable safety using the --unsafe-load-any-extension=y command line option. This feature is undocumented and classified as a hidden option (https://mail.python.org/pipermail/code-quality/2014-November/000439.html).
2) Disable safety using the pylint.rc setting unsafe-load-any-extensions=yes. This is recommended over option 1 and includes full documentation in the default pylint.rc file (created with --generate-rcfile).
3) Specifically list packages or modules names that you trust to be loaded by pylint in the pylint.rc file using the extension-pkg-whitelist= option.
4) Create a plugin to manipulate the AST (I have no idea how to effect this -- but it's regularly discussed on on the pylint mailing list).
We opted for Option 3. We added the following line to our project pylint.rc file:
extension-pkg-whitelist=lxml
#user590028, thanks a lot for your answer! I just ran into this same problem with the libraries win32api, win32evtlog, win32file, win32gui, and win32process, and your solution worked.
I used another method I think is worth posting here, which is to call pylint and pass the whitelisted packages as a parameter:
pylint --extension-pkg-whitelist=win32api,win32evtlog,win32file,win32gui,win32process myfile.py
For those of you using VS Code, it's a bit tricky to find where to put the command as I couldn't find my executable.
In VS Code;
click on File > Preferences > Settings.
Scroll down to "Python Configurations" in the left window
scroll down to "Python Linting: Mypy Args" in the right window
click on "Edit in settings.json" link
edit the json to include:
"--extension-pkg-whitelist="
I had to do all this because PyLint isn't executable from my Windows command line...
If you're using VS Code for Mac, this is what you need to do in order to edit the settings.json file:
Click on Code (i.e. the Visual Studio Code tab which is on the left of the 'File' tab) -> Preferences - > Settings
Scroll down to Extensions and click on Python in the list.
Click on any of the Edit in settings.json links. This opens up settings.json for editing.
Add the line "python.linting.pylintArgs": ["----extension-pkg-whitelist=1xml"].

How can I allow python users with a version lower than python2.7 to run my program that uses `sysconfig`?

I made a python program which uses the sysconfig module. How can I allow python users with a python version lower than python2.7 to also run that program? I can not find this library in PyPI.
Before I had also used argparse and this was also not installed by default in python versions lower than python2.7. But I could just add it in my requirements file because it can be downloaded using pip.
You'll need to write a version of your code that works without sysconfig - your code would look something like this:
try:
import sysconfig
HAS_SYSCONFIG = True
except ImportError:
HAS_SYSCONFIG = False
...
if HAS_SYSCONFIG:
# sysconfig code here
else:
# compatibility code here
You could also try backporting sysconfig to an earlier version of python and including it with your script, but that may be more work than it's worth.

How to tell if rosetta is installed?

I am working on a Python script that will rely on rosetta being installed. Rosetta is a dynamic binary translator for Mac OS X which allows many PowerPC applications to run on certain Intel-based Macintosh computers without modification. Is there anyway for to check the OS to see if rosetta is there?
Haven't got rosetta installed anymore but if I recall correctly it would give some kind of usage screen if you just type translate (rosetta command line). If so, something like this should work.
if os.system("/usr/libexec/oah/translate > /dev/null 2>&1"):
print "Not installed"
else:
print "Installed"
If you are really just trying to check whether something with a PPC dependency will likely run, you could do a loose check that the running CPU type is PPC or the running OS X version >= 10.4 and < 10.7 since those are the OS X versions where Rosetta is supported and, at least on 10.6, OS X will automatically prompt the user to install Rosetta when needed if it wasn't already installed. Note that the Darwin kernel versions are different from the OS X version number, i.e 10.4 -> Darwin 8, 10.5 -> 9, etc.:
>>> import os
>>> os.uname()
('Darwin', 'kitt.local', '11.4.0', 'Darwin Kernel Version 11.4.0: Mon Apr 9 19:32:15 PDT 2012; root:xnu-1699.26.8~1/RELEASE_X86_64', 'x86_64')
>>> un = os.uname()
>>> darwin_major_version = int(os.uname()[2].split('.')[0])
>>> cputype = un[4]
>>> can_run_ppc = cputype.startswith('ppc') or (darwin_major_version > 7 and darwin_major_version < 11)
>>> can_run_ppc
False
There is no official way to get this.
Rosetta works via a program called /usr/libexec/oah/translate. Officially, this is an implementation detail which is subject to change, and therefore shouldn't be relied on. However, we know that it never did change until 10.7, when Rosetta was killed completely, so it's safe despite the caveats. Maria Zverina's answer works for that (if you add the path), and it's probably the simplest. Or, maybe, just check for the presence of such a file instead of running it.
Alternatively, Rosetta came with Intel 10.4-10.6 (earlier versions of the OS were PPC-only and didn't have Intel). Again, officially you're never supposed to rely on OS version, instead using the appropriate APIs to check for features. But in this case, there don't seem to be any appropriate APIs, so maybe this is appropriate. Except for the caveat that you don't have to install Rosetta with 10.6, so this won't detect users who turned off the checkbox. If you want to do this:
import platform
release, versioninfo, machine = platform.mac_ver()
versionbits = [int(bit) for bit in release.split('.')]
rosetta = (versionbits < (10,7) and not machine.startswith('ppc'))
(Note that this is also "bad" because on some versions platform.mac_ver() does some hacky stuff that you're not supposed to do—the right way to get the OS X version bits is to call Gestalt. But mac_ver() is part of the standard library, so at least you can rely on it doing the hacky stuff as well as possible, and it being widely tested.)
If you're not actually after Rosetta, but whether you can run PPC either natively or via Rosetta, that's even simpler. All pre-10.7 versions that don't come with Rosetta are PPC; all 10.7+ versions can't run PPC period. So, just "release < '10.7'" does it. (Again, with the caveat that 10.6 can optionally skip Rosetta install.)
Try to run:
brew config
Rosetta 2: true

python 2.7 with mac osx lion: fn + delete and control + r

The sequences control+r and fn+delete that used to do recursive search / delete the following character do not work anymore in python 2.7 / Mac OSX Lion. Instead, a ~ appears each time I use fn+delete. I am using readline for tab completion (which also had to be changed according to python tab completion Mac OSX 10.7 (Lion)). Any ideas how to fix it?
Thanks,
Bruno
According to http://pypi.python.org/pypi/readline:
"Mac OS X, do not ship with GNU readline installed. The readline extension module in the standard library of Mac "system" Python uses NetBSD's editline (libedit) library instead, which is a readline replacement with a less restrictive software license."
So, you can install it with the command:
sudo easy_install readline
Else, you can use tcsh shortcuts; control + d to delete the following character and Ecp + p for history search.
For recursive search you can configure libedit by adding following line to ~/.editrc
bind ^R em-inc-search-prev
or right from your .pystartup file
readline.parse_and_bind("bind ^R em-inc-search-prev")

Categories