Python libgpiod vs gpiod packages in Linux? - python

I wrote a little test program in Python to manipulate GPIO pins on an an Intel Up Xtreme i11. First running under NixOS, I brought in the package as "libgpiod" and things are working. (MacOS package managers also know "libgpiod".) Then I tried to port this to an Ubuntu world on the same hardware. But apt and apt-get know nothing of libgpiod, they only know gpiod. pip3, too. So I installed gpiod, but the discrepancies mount up…
gpiod has a member "chip" rather than "Chip"
chip.get_line gets Error 22 for any small integer I can find.
What I lack is documentation. Is there something, somewhere, that clearly explains the distinction between these two packages that appear to be similar but are not? And what is actually the correct way of using the Ubuntu gpiod package in Python?
BTW I am running as root in both cases. Here's the code (gpiod version):
import gpiod, time
# pins
POWER = 9
chip=gpiod.chip('gpiochip0')
power=chip.get_line(POWER)
power.request(consumer="motor_movement", type=gpiod.LINE_REQ_DIR_OUT)
def run():
delay = 1.0
try:
#power.set_value(0)
while True:
power.set_value(1)
time.sleep(delay)
power.set_value(0)
time.sleep(delay)
finally:
cleanup()
def cleanup():
power.release()
if __name__ == "__main__":
run()

What you refer to as "libgpiod" library are system packages based on this C library.
From its documentation:
libgpiod
========
libgpiod - C library and tools for interacting with the linux GPIO
character device (gpiod stands for GPIO device)
Since linux 4.8 the GPIO sysfs interface is deprecated. User space should use
the character device instead. This library encapsulates the ioctl calls and
data structures behind a straightforward API.
The library also provides python3 bindings, which have probably been using.
On Ubuntu you would install everything you need issuing:
apt install python3-libgpiod.
Your code using this library should have looked like this:
import gpiod, time
# pins
POWER = 9
chip = gpiod.Chip('0')
power = chip.get_line(POWER)
power.request(consumer="motor_movement", type=gpiod.LINE_REQ_DIR_OUT)
def run():
delay = 1.0
try:
#power.set_value(0)
while True:
power.set_value(1)
time.sleep(delay)
power.set_value(0)
time.sleep(delay)
finally:
cleanup()
def cleanup():
power.release()
if __name__ == "__main__":
run()
For further usage examples see the examples section on the repo.
The python package gpiod available through pip from pypi.org is, "a pure Python library and has no dependencies on other packages". So no relation to the C library mentioned above.
See also this question for differences or advantages using one or the other.
There is a basic example provided as documentation.
To make your code working using python3-gpiod (the library installed through pip), you should modify as follows:
import gpiod, time
# pins
POWER = 9
chip=gpiod.chip('gpiochip0')
power=chip.get_line(POWER)
power_config = gpiod.line_request()
power_config.consumer = "motor_movement"
power_config.request_type = gpiod.line_request.DIRECTION_OUTPUT
power.request(power_config)
def run():
delay = 1.0
try:
#power.set_value(0)
while True:
power.set_value(1)
time.sleep(delay)
power.set_value(0)
time.sleep(delay)
finally:
cleanup()
def cleanup():
power.release()
if __name__ == "__main__":
run()
Alternatively try to use help(gpiod.line.get_line) or similar to troubleshoot your code.

Related

Why can't VSCode load MicroPython 'machine'?

I have installed the MicroPython IDE and Python extension in accordance with the VSCode instructions. This is my first use of VSCode and I have searched for a solution and failed. when I try to debug this code:
import machine
import time
led = machine.Pin(2, machine.Pin.OUT)
button = machine.Pin(14, machine.Pin.IN, machine.Pin.PULL_UP)
while button.value():
led.on()
time.sleep(1)
led.off()
time.sleep(1)
led.on()
I get this result: Module Not Found Error "no module named 'machine' "
I would very much appreciate your assistance.
Regards
John
You are dealing with two separate Python environments:
The "PC Python" (also called CPython) on your development machine
MicroPython running on embedded device like ESP32.
When testing on PC, you are running your code in CPython. But if you transfer it to the device, it runs within MicroPython. Both environments are very similar but some differences exist.
The machine module is one such difference. It exists only in MicroPython and allows actual access to the hardware. Thus, if you run your program in CPython, you get exactly the error you described.
You can either test your program on an actual device. Or you "mock" the machine module, i.e. you create that module as "dummy" implementation for testing on PC. It would at least need to contain the Pin() class, which could for example print the state changes to command line.
There is an example for such a mock on GitHub: https://github.com/tflander/esp32-machine-emulator
A minimal example for your case: Create machine.py containing:
class Pin:
IN = 0
OUT = 0
PULL_UP = 0
def __init__(self, number, mode=-1, pull=-1):
self.number = number
def on(self):
print('Pin %d switches ON' % self.number)
def off(self):
print('Pin %d switches OFF' % self.number)
def value(self):
return 1
# ... add other methods when needed ...
... and put it somewhere where your script can import it from.

How to use LibreOffice API (UNO) with Python + Windows?

This question is focused on Windows + LibreOffice + Python 3.
I've installed LibreOffice (6.3.4.2), also
pip install unoconv and pip install unotools (pip install uno is another unrelated library), but still I get this error after import uno:
ModuleNotFoundError: No module named 'uno'
More generally, and as an example of use of UNO, how to open a .docx document with LibreOffice UNO and export it to PDF?
I've searched extensively on this since a few days, but I haven't found a reproducible sample code working on Windows:
headless use of soffice.exe, see my question+answer Headless LibreOffice very slow to export to PDF on Windows (6 times slow than on Linux) and the notes on the answer: it "works" with soffice.exe --headless ... but something closer to a COM interaction (Component Object Model) would be useful for many applications, thus this question here
Related forum post, and LibreOffice: Programming with Python Scripts, but the way uno should be installed on Windows, with Python, is not detailed; also Detailed tutorial regarding LibreOffice to Python macro writing, especially for Calc
I've also tried this (unsuccessfully): Getting python to import uno / pyuno:
import os
os.environ["URE_BOOTSTRAP"] = r"vnd.sun.star.pathname:C:\Program Files\LibreOffice\program\fundamental.ini"
os.environ["PATH"] += r";C:\Program Files\LibreOffice\program"
import uno
In order to interact with LibreOffice, start an instance listening on a socket. I don't use COM much, but I think this is the equivalent of the COM interaction you asked about. This can be done most easily on the command line or using a shell script, but it can also work with a system call using a time delay and subprocess.
chdir "%ProgramFiles%\LibreOffice\program\"
start soffice -accept=socket,host=localhost,port=2002;urp;
Next, run the installation of python that comes with LibreOffice, which has uno installed by default.
"C:\Program Files\LibreOffice\program\python.exe"
>> import uno
If instead you are using an installation of Python on Windows that was not shipped with LibreOffice, then getting it to work with UNO is much more difficult, and I would not recommend it unless you enjoy hacking.
Now, here is all the code. In a real project, it's probably best to organize into classes, but this is a simplified version.
import os
import uno
from com.sun.star.beans import PropertyValue
def createProp(name, value):
prop = PropertyValue()
prop.Name = name
prop.Value = value
return prop
localContext = uno.getComponentContext()
resolver = localContext.ServiceManager.createInstanceWithContext(
"com.sun.star.bridge.UnoUrlResolver", localContext)
ctx = resolver.resolve(
"uno:socket,host=localhost,port=2002;urp;"
"StarOffice.ComponentContext")
smgr = ctx.ServiceManager
desktop = smgr.createInstanceWithContext(
"com.sun.star.frame.Desktop", ctx)
dispatcher = smgr.createInstanceWithContext(
"com.sun.star.frame.DispatchHelper", ctx)
filepath = r"C:\Users\JimStandard\Desktop\Untitled 1.docx"
fileUrl = uno.systemPathToFileUrl(os.path.realpath(filepath))
uno_args = (
createProp("Minimized", True),
)
document = desktop.loadComponentFromURL(
fileUrl, "_default", 0, uno_args)
uno_args = (
createProp("FilterName", "writer_pdf_Export"),
createProp("Overwrite", False),
)
newpath = filepath[:-len("docx")] + "pdf"
fileUrl = uno.systemPathToFileUrl(os.path.realpath(newpath))
try:
document.storeToURL(fileUrl, uno_args) # Export
except ErrorCodeIOException:
raise
try:
document.close(True)
except CloseVetoException:
raise
Finally, since speed is a concern, using a listening instance of LibreOffice can be slow. To do this faster, move the code into a macro. APSO provides a menu to organize Python macros. Then call the macro like this:
soffice "vnd.sun.star.script:myscript.py$name_of_maindef?language=Python&location=user"
In macros, obtain the document objects from XSCRIPTCONTEXT rather than the resolver.

How do you get a module to function, and be recognized from within Choreographe on a Nao robot?

I'm using a Nao robot, and the python SDK, and I'm trying to create my own module for it. Right now it is just a dummy module with one function: getJoke().
I have my file tellAJoke.py located under the path /home/nao/tellAJoke.py and I have updated the autoload.ini file to include the following:
[python]
/home/nao/tellAJoke.py
When I boot up the robot, it says what it normally does on startup, but also says what my getJoke() function returns, "This is a bad joke".
I'm not sure what I'm doing wrong here. When I ssh onto the robot, and run the code it runs just fine, but never when I want to import the module with ALProxy in Choreographe.
EDIT: I added the actual dummy code I have.
from naoqi import ALBroker
from naoqi import ALModule
from naoqi import ALProxy
import sys
class JokerModule(ALModule):
"""Tells you a random joke"""
def __init__(self, name):
print "WE HAVE INITED"
self.tts = ALProxy("ALTextToSpeech")
ALModule.__init__(self, name)
global memory
memory = ALProxy("ALMemory")
memory.subscribeToEvent("SayingJoke", "Joker", "getJoke")
def getJoke(self, *_args):
"""Returns a joke"""
self.tts.say("Joke time!")
def main():
"""main entry point"""
pip = '192.168.1.104'
pport = 9559
myBroker = ALBroker("myBroker", '0.0.0.0', 0, pip, pport)
global Joker
Joker = JokerModule("Joker")
speechProxy = ALProxy("ALTextToSpeech")
Joker.getJoke()
if __name__ == '__main__':
main()
Here is a guide on making services (a.k.a. "modules", but that term is confusing because it has another meaning in Python): http://doc.aldebaran.com/2-4/dev/libqi/guide/py-service.html (this doc is for NAOqi 2.4 but things should work mostly the same for 2.1, which is more often used on NAO)
But, you might want to try Robot Jumpstarter, that contains templates for various typical projects, including a python service (that works as explained in the document above).
clone it and run python jumpstart.py python-service tell-a-joke TellAJoke
... and it will generate a project that you can:
install on the robot witch Choregraphe
run in standalone with python tell-a-joke/app/scripts/tellajoke.py --qi-url your-naos-ip
... and in both cases you will be able to call it from from Choregraphe boxes etc.
(edit)
Now that you posted your code - in this specific case your problem is just that after Joker.getJoke(), your program reaches the end and terminates. The usual "modern" way of doing that would be with a qi.Application() that would .run() (all that is done in the jumpstarter template). You could do a while True: sleep(1) or something, which is not very pretty but would work (I recommend migrating to NAOqi 2, and instead of using ALProxy and ALBroker, use session.service and qi.Application ... the two are interoperable)

Obtain Active window using Python

I would like to get the active window on the screen using python.
For example, the management interface of the router where you enter the username and password as admin
That admin interface is what I want to capture using python to automate the entry of username and password.
What imports would I require in order to do this?
On windows, you can use the python for windows extensions (http://sourceforge.net/projects/pywin32/):
from win32gui import GetWindowText, GetForegroundWindow
print GetWindowText(GetForegroundWindow())
Below code is for python 3:
from win32gui import GetWindowText, GetForegroundWindow
print(GetWindowText(GetForegroundWindow()))
(Found this on http://scott.sherrillmix.com/blog/programmer/active-window-logger/)
Thanks goes to the answer by Nuno André, who showed how to use ctypes to interact with Windows APIs. I have written an example implementation using his hints.
The ctypes library is included with Python since v2.5, which means that almost every user has it. And it's a way cleaner interface than old and dead libraries like win32gui (last updated in 2017 as of this writing). ((Update in late 2020: The dead win32gui library has come back to life with a rename to pywin32, so if you want a maintained library, it's now a valid option again. But that library is 6% slower than my code.))
Documentation is here: https://docs.python.org/3/library/ctypes.html (You must read its usage help if you wanna write your own code, otherwise you can cause segmentation fault crashes, hehe.)
Basically, ctypes includes bindings for the most common Windows DLLs. Here is how you can retrieve the title of the foreground window in pure Python, with no external libraries needed! Just the built-in ctypes! :-)
The coolest thing about ctypes is that you can Google any Windows API for anything you need, and if you want to use it, you can do it via ctypes!
Python 3 Code:
from typing import Optional
from ctypes import wintypes, windll, create_unicode_buffer
def getForegroundWindowTitle() -> Optional[str]:
hWnd = windll.user32.GetForegroundWindow()
length = windll.user32.GetWindowTextLengthW(hWnd)
buf = create_unicode_buffer(length + 1)
windll.user32.GetWindowTextW(hWnd, buf, length + 1)
# 1-liner alternative: return buf.value if buf.value else None
if buf.value:
return buf.value
else:
return None
Performance is extremely good: 0.01 MILLISECONDS on my computer (0.00001 seconds).
Will also work on Python 2 with very minor changes. If you're on Python 2, I think you only have to remove the type annotations (from typing import Optional and -> Optional[str]). :-)
Enjoy!
Win32 Technical Explanations:
The length variable is the length of the actual text in UTF-16 (Windows Wide "Unicode") CHARACTERS. (It is NOT the number of BYTES.) We have to add + 1 to add room for the null terminator at the end of C-style strings. If we don't do that, we would not have enough space in the buffer to fit the final real character of the actual text, and Windows would truncate the returned string (it does that to ensure that it fits the super important final string Null-terminator).
The create_unicode_buffer function allocates room for that many UTF-16 CHARACTERS.
Most (or all? always read Microsoft's MSDN docs!) Windows APIs related to Unicode text take the buffer length as CHARACTERS, NOT as bytes.
Also look closely at the function calls. Some end in W (such as GetWindowTextLengthW). This stands for "Wide string", which is the Windows name for Unicode strings. It's very important that you do those W calls to get proper Unicode strings (with international character support).
PS: Windows has been using Unicode for a long time. I know for a fact that Windows 10 is fully Unicode and only wants the W function calls. I don't know the exact cutoff date when older versions of Windows used other multi-byte string formats, but I think it was before Windows Vista, and who cares? Old Windows versions (even 7 and 8.1) are dead and unsupported by Microsoft.
Again... enjoy! :-)
UPDATE in Late 2020, Benchmark vs the pywin32 library:
import time
import win32ui
from typing import Optional
from ctypes import wintypes, windll, create_unicode_buffer
def getForegroundWindowTitle() -> Optional[str]:
hWnd = windll.user32.GetForegroundWindow()
length = windll.user32.GetWindowTextLengthW(hWnd)
buf = create_unicode_buffer(length + 1)
windll.user32.GetWindowTextW(hWnd, buf, length + 1)
return buf.value if buf.value else None
def getForegroundWindowTitle_Win32UI() -> Optional[str]:
# WARNING: This code sometimes throws an exception saying
# "win32ui.error: No window is is in the foreground."
# which is total nonsense. My function doesn't fail that way.
return win32ui.GetForegroundWindow().GetWindowText()
iterations = 1_000_000
start_time = time.time()
for x in range(iterations):
foo = getForegroundWindowTitle()
elapsed1 = time.time() - start_time
print("Elapsed 1:", elapsed1, "seconds")
start_time = time.time()
for x in range(iterations):
foo = getForegroundWindowTitle_Win32UI()
elapsed2 = time.time() - start_time
print("Elapsed 2:", elapsed2, "seconds")
win32ui_pct_slower = ((elapsed2 / elapsed1) - 1) * 100
print("Win32UI library is", win32ui_pct_slower, "percent slower.")
Typical result after doing multiple runs on an AMD Ryzen 3900x:
My function: 4.5769994258880615 seconds
Win32UI library: 4.8619983196258545 seconds
Win32UI library is 6.226762715455125 percent slower.
However, the difference is small, so you may want to use the library now that it has come back to life (it had previously been dead since 2017). But you're going to have to deal with that library's weird "no window is in the foreground" exception, which my code doesn't suffer from (see the code comments in the benchmark code).
Either way... enjoy!
The following script should work on Linux, Windows and Mac. It is currently only tested on Linux (Ubuntu Mate Ubuntu 15.10).
Prerequisites
For Linux:
Install wnck (sudo apt-get install python-wnck on Ubuntu, see libwnck.)
For Windows:
Make sure win32gui is available
For Mac:
Make sure AppKit is available
The script
#!/usr/bin/env python
"""Find the currently active window."""
import logging
import sys
logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',
level=logging.DEBUG,
stream=sys.stdout)
def get_active_window():
"""
Get the currently active window.
Returns
-------
string :
Name of the currently active window.
"""
import sys
active_window_name = None
if sys.platform in ['linux', 'linux2']:
# Alternatives: https://unix.stackexchange.com/q/38867/4784
try:
import wnck
except ImportError:
logging.info("wnck not installed")
wnck = None
if wnck is not None:
screen = wnck.screen_get_default()
screen.force_update()
window = screen.get_active_window()
if window is not None:
pid = window.get_pid()
with open("/proc/{pid}/cmdline".format(pid=pid)) as f:
active_window_name = f.read()
else:
try:
from gi.repository import Gtk, Wnck
gi = "Installed"
except ImportError:
logging.info("gi.repository not installed")
gi = None
if gi is not None:
Gtk.init([]) # necessary if not using a Gtk.main() loop
screen = Wnck.Screen.get_default()
screen.force_update() # recommended per Wnck documentation
active_window = screen.get_active_window()
pid = active_window.get_pid()
with open("/proc/{pid}/cmdline".format(pid=pid)) as f:
active_window_name = f.read()
elif sys.platform in ['Windows', 'win32', 'cygwin']:
# https://stackoverflow.com/a/608814/562769
import win32gui
window = win32gui.GetForegroundWindow()
active_window_name = win32gui.GetWindowText(window)
elif sys.platform in ['Mac', 'darwin', 'os2', 'os2emx']:
# https://stackoverflow.com/a/373310/562769
from AppKit import NSWorkspace
active_window_name = (NSWorkspace.sharedWorkspace()
.activeApplication()['NSApplicationName'])
else:
print("sys.platform={platform} is unknown. Please report."
.format(platform=sys.platform))
print(sys.version)
return active_window_name
print("Active window: %s" % str(get_active_window()))
For Linux users:
All the answers provided required additional modules like "wx" that had numerous errors installing ("pip" failed on build), but I was able to modify this solution quite easily -> original source. There were bugs in the original (Python TypeError on regex)
import sys
import os
import subprocess
import re
def get_active_window_title():
root = subprocess.Popen(['xprop', '-root', '_NET_ACTIVE_WINDOW'], stdout=subprocess.PIPE)
stdout, stderr = root.communicate()
m = re.search(b'^_NET_ACTIVE_WINDOW.* ([\w]+)$', stdout)
if m != None:
window_id = m.group(1)
window = subprocess.Popen(['xprop', '-id', window_id, 'WM_NAME'], stdout=subprocess.PIPE)
stdout, stderr = window.communicate()
else:
return None
match = re.match(b"WM_NAME\(\w+\) = (?P<name>.+)$", stdout)
if match != None:
return match.group("name").strip(b'"')
return None
if __name__ == "__main__":
print(get_active_window_title())
The advantage is it works without additional modules. If you want it to work across multiple platforms, it's just a matter of changing the command and regex strings to get the data you want based on the platform (with the standard if/else platform detection shown above sys.platform).
On a side note: import wnck only works with python2.x when installed with "sudo apt-get install python-wnck", since I was using python3.x the only option was pypie which I have not tested. Hope this helps someone else.
There's really no need to import any external dependency for tasks like this. Python comes with a pretty neat foreign function interface - ctypes, which allows for calling C shared libraries natively. It even includes specific bindings for the most common Win32 DLLs.
E.g. to get the PID of the foregorund window:
import ctypes
from ctypes import wintypes
user32 = ctypes.windll.user32
h_wnd = user32.GetForegroundWindow()
pid = wintypes.DWORD()
user32.GetWindowThreadProcessId(h_wnd, ctypes.byref(pid))
print(pid.value)
In Linux under X11:
xdo_window_id = os.popen('xdotool getactivewindow').read()
print('xdo_window_id:', xdo_window_id)
will print the active window ID in decimal format:
xdo_window_id: 67113707
Note xdotool must be installed first:
sudo apt install xdotool
Note wmctrl uses hexadecimal format for window ID.
This only works on windows
import win32gui
import win32process
def get_active_executable_name():
try:
process_id = win32process.GetWindowThreadProcessId(
win32gui.GetForegroundWindow()
)
return ".".join(psutil.Process(process_id[-1]).name().split(".")[:-1])
except Exception as exception:
return None
I'll recommend checking out this answer for making it work on linux, mac and windows.
I'd been facing same problem with linux interface (Lubuntu 20).
What I do is using wmctrl and execute it with shell command from python.
First, Install wmctrl
sudo apt install wmctrl
Then, Add this code :
import os
os.system('wmctrl -a "Mozilla Firefox"')
ref wmctrl :
https://askubuntu.com/questions/21262/shell-command-to-bring-a-program-window-in-front-of-another
In Linux:
If you already have installed xdotool, you can just use:
from subprocess import run
def get__focused_window():
return run(['xdotool', 'getwindowfocus', 'getwindowpid', 'getwindowname'], capture_output=True).stdout.decode('utf-8').split()
While I was writing this answer I've realised that there were also:
A reference about "xdotool" on comments
& another slightly similar "xdotool" answer
So, I've decided to mention them here, too.
Just wanted to add in case it helps, I have a function for my program (It's a software for my PC's lighting I have this simple few line function:
def isRunning(process_name):
foregroundWindow = GetWindowText(GetForegroundWindow())
return process_name in foregroundWindow
Try using wxPython:
import wx
wx.GetActiveWindow()

Tab-completion in Python interpreter in OS X Terminal

Several months ago, I wrote a blog post detailing how to achieve tab-completion in the standard Python interactive interpreter--a feature I once thought only available in IPython. I've found it tremendously handy given that I sometimes have to switch to the standard interpreter due to IPython unicode issues.
Recently I've done some work in OS X. To my discontent, the script doesn't seem to work for OS X's Terminal application. I'm hoping some of you with experience in OS X might be able to help me trouble-shoot it so it can work in Terminal, as well.
I am reproducing the code below
import atexit
import os.path
try:
import readline
except ImportError:
pass
else:
import rlcompleter
class IrlCompleter(rlcompleter.Completer):
"""
This class enables a "tab" insertion if there's no text for
completion.
The default "tab" is four spaces. You can initialize with '\t' as
the tab if you wish to use a genuine tab.
"""
def __init__(self, tab=' '):
self.tab = tab
rlcompleter.Completer.__init__(self)
def complete(self, text, state):
if text == '':
readline.insert_text(self.tab)
return None
else:
return rlcompleter.Completer.complete(self,text,state)
#you could change this line to bind another key instead tab.
readline.parse_and_bind('tab: complete')
readline.set_completer(IrlCompleter('\t').complete)
# Restore our command-line history, and save it when Python exits.
history_path = os.path.expanduser('~/.pyhistory')
if os.path.isfile(history_path):
readline.read_history_file(history_path)
atexit.register(lambda x=history_path: readline.write_history_file(x))
Note that I have slightly edited it from the version on my blog post so that the IrlCompleter is initialized with a true tab, which seems to be what is output by the Tab key in Terminal.
This should work under Leopard's python:
import rlcompleter
import readline
readline.parse_and_bind ("bind ^I rl_complete")
Whereas this one does not:
import readline, rlcompleter
readline.parse_and_bind("tab: complete")
Save it in ~/.pythonrc.py and execute in .bash_profile
export PYTHONSTARTUP=$HOME/.pythonrc.py
here is a full cross platform version of loading tab completion for Windows/OS X/Linux in one shot:
#Code UUID = '9301d536-860d-11de-81c8-0023dfaa9e40'
import sys
try:
import readline
except ImportError:
try:
import pyreadline as readline
# throw open a browser if we fail both readline and pyreadline
except ImportError:
import webbrowser
webbrowser.open("http://ipython.scipy.org/moin/PyReadline/Intro#line-36")
# throw open a browser
#pass
else:
import rlcompleter
if(sys.platform == 'darwin'):
readline.parse_and_bind ("bind ^I rl_complete")
else:
readline.parse_and_bind("tab: complete")
From http://www.farmckon.net/?p=181
To avoid having to use more GPL code, Apple doesn't include a real readline. Instead it uses the BSD-licensed libedit, which is only mostly-readline-compatible. Build your own Python (or use Fink or MacPorts) if you want completion.
This works for me on both Linux bash and OS X 10.4
import readline
import rlcompleter
readline.parse_and_bind('tab: complete')
If after trying the above, it still doesn't work, then try to execute in the shell:
sudo easy_install readline
Then, create ~/.profile file with the content:
export PYTHONSTARTUP=$HOME/.pythonrc.py
and a ~/.pythonrc.py file with the content:
try:
import readline
except:
print ("Module readline is not available.")
else:
import rlcompleter
readline.parse_and_bind("tab: complete")
Thanks to Steven Bamford for the easy_install tip, and Nicolas for the file content.
The documented way to tell libedit (the Mac OS semi-readline) from the real one is:
if "libedit" in readline.doc:
pass # Mac case
else:
pass # GNU readline case
After crashing into many issues dealing with Python (2 and 3) on FreeBSD, I finally got a proper extension to work using libedit directly as the completer for Python.
The basic issue with libedit/readline is that Python's completion and input was heavily bent towards GNU readline... Sadly, this is actually not a particularly good interface. It requires a giant number of globals in C and does not work well on an "instance" basis.
Solution:
https://github.com/mark-nicholson/python-editline
This is a true separate python extension which directly links to libedit using the actual "libedit" interface -- not the readline glue on the side.
I have tested it pretty thoroughly on Ubuntu, FreeBSD, OpenBSD, NetBSD and MacOS -- results are posted in the readme. The c-code is very clean and has virtually no platform dependent bits -- unlike the readline.c module in Python.
Notes:
It works on Python3 > 3.2.
It is NOT a drop-in replacement for 'import readline' in other scripts, but those scripts can be adjusted easily.
It can co-exist with readline.so -- there is code for a sitecustomize.py file which enables the selection.
It can use a distribution 'libedit.so', a custom built one or libedit built into the extension itself.

Categories

Resources