Search Results: "cas"

28 July 2012

Alberto Garc a: GUADEC 2012

Third day of GUADEC already. And in Coru a! Coru a This is a very special city for me. I came here in 1996 to study Computer Science. Here I discovered UNIX for the first time, and spent hours learning how to use it. It s funny to see now those old UNIX servers being displayed in a small museum in the auditorium where the main track takes place. It was also here where I learnt about free software, installed my first Debian system, helped creating the local LUG and met the awesome people that founded Igalia with me. Then we went international, but our headquarters and many of our people are still here so I guess we can still call this home. So, needless to say, we are very happy to have GUADEC here this time. I hope you all are enjoying the conference as much as we are. I m quite satisfied with how it s been going so far, the local team has done a good job organising everything and taking care of lots of details to make the life of all attendees easier. I especially want to stress all the effort put into the network infrastructure, one of the best that I remember in a GUADEC conference. At Igalia we ve been very busy lately. We re putting lots of effort in making WebKit better, but our work is not limited to that. Our talks this year show some of the things we ve been doing: We are also coordinating 4 BOFs (a11y, GNOME OS, WebKit and Grilo) and hosted a UX hackfest in our offices before the conference. And we have a booth next to the info desk where you can get some merchandising and see our interactivity demos. WebKitGTK+ In case you missed the conference this year, all talks are being recorded and the videos are expected to be published really soon (before the end of the conference). So enjoy the remaining days of GUADEC, and enjoy Coru a! And of course if you re staying after the conference and want to know more about the city or about Galicia, don t hesitate to ask me or anyone from the local team, we ll be glad to help you. Queimada

24 July 2012

Wouter Verhelst: Runner scripts

Bert Desmet blogs about having to run a script more than once per minute, and using a runner script (started from cron) to do that. Unfortunately, his implementation contains a race condition:
if ! ps -p $(cat /path/to/runner.pid);
  then
    echo $$ > /path/to/runner.pid
If the script is accidentally started twice on approximately the same moment, chances are pretty high you'll see them both executing the ps call at approximately the same time, concluding that no other script is running, and then both executing the echo call at the same time. Result: your script will be running twice, yet you'll have only one pid file. Of course, if you do indeed run the script only from cron, and just once every 30 minutes, as in Bert's case, chances that you'll hit this race condition are pretty low. However, race conditions are bad, especially if they're easy to avoid.
set -o noclobber
if ! echo $$ > /var/run/runner.pid
then
  echo pidfile exists; exiting
  exit 0
fi
This code uses the noclobber shell option, which causes the shell to issue an error state if the file we try to overwrite with > already exists, and to do so in an atomic operation (so, no race condition there: either the file exists and we fail, or the file does not exist yet and we create it with our PID). Of course, that leaves out the check for is the other process running? Adding a second check for that is fairly complex to do right, as this cannot be done in an atomic operation; so it's usually better use exit traps to clean up the file:
set -o noclobber
if ! echo $$ > /var/run/runner.pid
then
  echo pidfile exists; exiting
  exit 0
fi
trap EXIT rm -f /var/run/runner.pid
This will fail if the system dies when the script is running, but then since we use /var/run (which is cleared on bootup, either because it's a tmpfs or because the initscripts clean it) this means the file should only ever be stale if some genius kills our process with kill -9 or kill -11.

23 July 2012

C.J. Adams-Collier: You might on occasion confuse us with professionals

If we keep this up, you might. image

22 July 2012

Matthew Palmer: Podcasting protip

Don t spend the first two minutes of the first episode of your podcast telling everyone what a micropodcast is, and how iTunes only lets you have 20MB per episode. The only exception to this might be if you were making a podcast about podcasting. Which I wasn t. That is all.

Sylvestre Ledru: Who is in control of LLVM/Clang ?

During my various chats and presentations on LLVM/Clang, one of the common criticism on LLVM/Clang is about the Apple's (supposed?) control on the platform. Even if I am not concerned by this concern, here are some statistics about the development. Basically, I used the git history to build a list of all the author. If domain names were clear (ex: @google.com, @apple.com, etc), I used them for the paternity of the commit. If there were not clear (ex: @gmail.com, @me.com), I dig on the web to find the employer (Linkedin was my friend). Of course, identifying individuals are pretty tough. Their percentages can be considered as a minimal figure. Full history 10 000 commits (about a year)
3 000 commits (~a month)
To sum up, we can easily see that the percentage of contributions of Apple is decreasing over the time.
However, we all know that the number of commits is not the best metric to measure the control of one on a project. Source: Processed data (please contact me in case of mistake)

Mark Brown: regmap updates in 3.5

A surprisingly large series of updates for regmap this time, mostly due to all the work Stephen Warren has done to add support for MMIO buses. This wasn t really the target for the framework but it turns out that there s a reasonable number of cases where it s very helpful to use the register cache support to allow the register map to remain available while the device is suspended.

21 July 2012

Enrico Zini: Giving away distromatch

Giving away distromatch at last year's Fosdem I tried to inject a lot of energy into distromatch but shortly afterwards I've had to urgently rewrite the nm.debian.org website. After Lars Wirzenius GTDFH talks in Bologna and Varese I wrote a tool which, among other things, is able to scan my home dir and list how many projects I'm working on. The output was scary. Like, they are too many. Like, I couldn't even recite the list out of memory. And since I couldn't do that, I had no idea there were so many. And I kept being stressful because I couldn't manage to take care of them all properly. Now that I became conscious of the situation, it's time to deal with it like a grown up, and politely back off from some of my irresponsible responsibilities. Distromatch is one of them. It had just started as a proof of concept prototype, and I had the vision that it could be the basis for a fantastic culture of sharing and exchange of information across distributions. I need to distinguish the vision from the responsibility. I still have that vision for distromatch, but I cannot take responsibility for making it happen. So I am giving it up to anyone who has the time and resources to pick up that responsibility. Current status It works well enough as a prototype. I believe it can successfully map a large enough slice of packages, that one can prototype stuff based on it. I have for example used it to export the Debtags categories for other distros, and the resulting file looked big enough to be used for prototyping category-based features on distributions that don't have them yet. I think it also works well enough to support a few common use cases, like sharing screenshots, or doing most of the work of converting dependency lists from a distro to another. And finally, anyone can deploy it, and work on it. Existing data sources Everything I index in the Debian distromatch deployment is available at http://dde.debian.net/exports/distromatch/. The rpm-based data in there comes from an export script I wrote that runs on Sophie, but which I cannot maintain properly. This is an experimental export of Fedora and OpenSUSE data: http://tmp.vuntz.net/misc/distromatch/distromatch-opensuse-fedora.tar All existing export scripts are found in distromatch git repo on gitorious. Contacts I gathered at Fosdem At Fosdem I devoted quite some work to get contacts from all possible distributions and software repositories, so that distromatch could be hooked into them. Here is a dump of what I have collected: Some of those contacts may have "expired" in the meantime: I wouldn't assume all of them still remember talking with me, although most probably still do. My commitment for the time being I am happy to commit, at the moment, to maintaining a working data export for Debian data. I can take responsibility for making it so that the Debian data for it stays up to date, and to fix it asap if it isn't the case. I hope that now someone can take distromatch over from me, and make it grow to achieve its great potential.

Siegfried Gevatter: A gentle introduction to Zeitgeist s Python API

In this post I ll make a quick introduction by example on how to use Zeitgeist s Python API for good and profit. If you re interested in using Zeitgeist from C instead, see the libzeitgeist examples; to use it with C++/Qt, Trever s a web browser in 4 steps may be of interest. First things first In case you re not familiar with Zeitgeist, it may prove helpful to first read Mikkel s introduction to Zeitgeist post. If that s too much to read, just should know that Zeitgeist is an event log. Like the history in your browser, it keeps track of what websites you open at which point in time. It also keeps track of when you close them, and of what browser you used, since it s a system-wide service. Furthermore, it does the same for files, conversations, e-mails, and anything else you want to insert into it. So Zeitgeist is a database of events, and an event can be pretty much anything. But what does it look like? It s main attributes are the following: Additionally, each event has one or more subjects, which have the following attributes: Retrieving recent data Okay, so let s say you want to know the last song you ve listened to (if you have a Zeitgeist-enabled music player). It s a simple as:
from zeitgeist.client import ZeitgeistClient
from zeitgeist.datamodel import *
 
zeitgeist = ZeitgeistClient()
 
def on_events_received(events):
    if events:
        song = events[0]
        print "Last song: %s" % song.subjects[0].uri
    else:
        print "You haven't listened to any songs."
 
template = Event.new_for_values(subject_interpretation=Interpretation.AUDIO)
zeitgeist.find_events_for_template(template, on_events_received, num_events=1)
 
# Start a mainloop - note: the Qt mainloop also works
from gi.repository import GLib
GLib.MainLoop().run()
This may need some explaining. We start by importing the Zeitgeist client and some associated data structures (such as Event and Interpretation), and create an instance of the client. The ZeitgeistClient class is a wrapper around Zeitgeist s D-Bus API which not only makes it much nicer to use, but also makes it easy to install monitors (as we will see later) and provides convenient functionality such as automatic reconnection if the connection to the engine is lost. To query for the most recent song, we just need to create a template with the restrictions we want to impose and submit it to Zeitgeist using the find_events_for_template call. If you haven t read Mikkel s post yet, please do so, as it introduces the structure of events and subjects (in short: an event has a timestamp, some other properties, and one or more subjects representing the resources -files, websites, people - involved in the event). The Python API is inherently asynchronous (if for some reason you need a synchronous API, you may still use the lower level ZeitgeistDBusInterface class), so we need to define a callback function to handle the results we receive from the Zeitgeist engine. Finally, we need to create a main loop so the asynchronous functions can run.
Now this was a pretty simple example. Let s make it more interesting. One song isn t much, so let s get the 5 most recent songs. Also, now we want both songs and videos. The first part is pretty easy, we just need to change the num_events parameter. For the second extension, we have to change the event template. In fact, now we need two different event templates and the find_events_for_templates function, which takes an arbitrary number of event templates and ORs them. The result is as follows:
from zeitgeist.client import ZeitgeistClient
from zeitgeist.datamodel import *
 
zeitgeist = ZeitgeistClient()
 
def on_events_received(events):
    for event in events:
        print "- %s" % event.subjects[0].uri
 
tmpl1 = Event.new_for_values(subject_interpretation=Interpretation.AUDIO)
tmpl2 = Event.new_for_values(subject_interpretation=Interpretation.VIDEO)
zeitgeist.find_events_for_templates([tmpl1, tmpl2],
                                    on_events_received, num_events=5)
 
# Start a mainloop
from gi.repository import GLib
GLib.MainLoop().run()
This will work, but unless you re lucky you re likely to get some duplicate line. Why is this? Well, other than that you may have used the same file twice, don t forget that what you are requesting are actually events. If you ve started playing a given song, you probably also stopped playing it, so that s actually two of them (an AccessEvent and a LeaveEvent). Since this isn t what we want, we ll change the query a bit:
zeitgeist.find_events_for_templates(
    [tmpl1, tmpl2],
    on_events_received,
    num_events=5,
    result_type=ResultType.MostRecentSubjects,
    storage_state=StorageState.Available)
By requesting the most recent subjects, vs. the most recent events, we can filter out events with duplicate URI. See the ResultType documentation for other modes you can use. Note particularly the MostPopularSubjects result type. I also used the chance to introduce the storage_state parameter. This one will filter out events for files Zeitgeist knows aren t available (this mostly means online resources won t be shown if you don t have a network connnection; there s also support for handling external storage media, but because of problems with GIO this is currently disabled). Last but not least, the find_events_for_* methods also accept a timerange parameter. It defaults to TimeRange.until_now(), but you may change it to TimeRange.always() (if for some reason you re working with events in the future) or to any other time range of your choice. Here it s important to note that Zeitgeist s timestamps use millisecond precision.
For more advanced queries, you can use more complex combinations of events and subject templates. The rule to keep in mind here is that events are OR d and subjects are ANDed. Additionally, some field (actor, origin, mimetype, uri and current_uri) may be prefixed with an exclamation mark ( ! ) for NOT, or you may append an asterisc ( * ) to them for prefix search. You can even combine the two operators together. Here s an example of a template you could build:
subj1 = Subject.new_for_values(interpretation=Interpretation.SOURCE_CODE,
                               uri="file:///home/rainct/Development/*")
subj2 = Subject.new_for_values(uri="!file:///home/rainct/Development/zeitgeist/*")
tmpl1 = Event.new_for_values(interpretation=Interpretation.MODIFY_EVENT,
                             subjects=[subj1, subj2])
templates = [tmpl1]
In my case, this template would fetch a list of the source code files I modified most recently but excluding those related to the Zeitgeist project. Working with big sets of data In case you re trying to do something crazy, you may end up with a Zeitgeist query complaining that it exceeded the memory limit. You re not supposed to do that. Instead, we provide some methods for working with large collections of events.
from zeitgeist.client import ZeitgeistClient
from zeitgeist.datamodel import *
 
zeitgeist = ZeitgeistClient()
 
def on_events_received(events):
    for event in events:
        print '- %s' % event.subjects[0].uri
 
def on_ids_received(event_ids):
    print 'A total of %d source code files were found.' % len(event_ids)
    print 'Fetching the first 100...'
    zeitgeist.get_events(event_ids[:100], on_events_received)
 
tmpl = Event.new_for_values(subject_interpretation=Interpretation.SOURCE_CODE)
zeitgeist.find_event_ids_for_templates(
    [tmpl],
    on_ids_received,
    num_events=10000, # you can use 0 for "all events", but do you really need to?
    timerange=TimeRange.from_seconds_ago(3600*24*30*3),
    result_type=ResultType.MostPopularSubjects)
 
# Start a mainloop
from gi.repository import GLib
GLib.MainLoop().run()
And there you have the source code files you worked with during the last 3 months, ordered from most to least popular (popularity is measured counting the number of events; for more precision, maybe you could limit the results to events with interpretation AccessEvent). Why do we provide this mechanism instead of querying with a simple offset? Well, this avoids problems when the log changes (events are inserted or deleted). Have you ever been exploring the latest posts in some website, and as you change to the next page some of the results from the previous page show up again (because new posts have been added in the meantime)? With Zeitgeist this won t happen. Receiving information in real time At this point you re an expert at requesting all sorts of data from Zeitgeist, but now you want to show a list of the last kitten images you ve viewed, updated in real time. Don t worry, Zeitgeist can provide for this:
from zeitgeist.client import ZeitgeistClient
from zeitgeist.datamodel import *
 
zeitgeist = ZeitgeistClient()
 
def on_insert(time_range, events):
    # do awesome stuff with the events here
    print events
 
def on_delete(time_range, event_ids):
    # a previously inserted event was deleted
    print event_ids
 
templates = [Event.new_for_values(subject_uri='file:///home/user/kittens/*',
                                  subject_interpretation=Interpretation.IMAGE)]
zeitgeist.install_monitor(TimeRange.always(), templates, on_insert, on_delete)
 
# Start a mainloop
from gi.repository import GLib
GLib.MainLoop().run()
It s important to note that on_delete won t be called when an image is deleted (that d be a newly inserted event with interpretation=DELETE_EVENT); rather, it s called when a previously inserted event is deleted (for example, using the forget recent history option in Activity Log Manager). In case you re curious: for best performance, this doesn t actually use D-Bus signals. Instead, this little call will setup a D-Bus object behind the scenes and register it with the Zeitgeist engine, so it can notify said object when (and only when) an event of its interest is registered. To stop receiving notifications for a template, you ll need the save the object returned by the install_monitor call:
m = zeitgeist.install_monitor(TimeRange.always(), templates, on_insert, on_delete)
zeitgeist.remove_monitor(m)
Pro Tip: You can use the Zeitgeist Explorer GUI to quickly try out different queries (note: it s still work in progress, so much funcionality is missing, but it does work somewhat). Contextual awesomeness: finding related events By now you re familiar with retrieving events and keeping them up to date. Now it s time for a little secret:
import time
from zeitgeist.client import ZeitgeistClient
from zeitgeist.datamodel import *
 
zeitgeist = ZeitgeistClient()
 
def on_related_received(uris):
    print 'Related URIs:'
    for uri in uris:
        print ' - %s' % uri
 
query_templates = [Event.new_for_values(
    subject_interpretation=Interpretation.SOURCE_CODE,
    subject_uri='file:///home/rainct/Development/zeitgeist/*',
    subject_mimetype="text/x-vala")]
 
result_templates = [Event.new_for_values(
    subject_interpretation=Interpretation.WEBSITE,
    subject_manifestation=Manifestation.WEB_DATA_OBJECT)]
 
now = time.time()*1000
zeitgeist.find_related_uris_for_events(
    query_templates,
    on_related_received,
    time_range=TimeRange(now - 1000*3600*24*30*6, now),
    result_event_templates=result_templates,
    num_events=10)
 
# Start a mainloop
from gi.repository import GLib
GLib.MainLoop().run()
This little query example will return up to 10 websites I used at the same time as the Vala files inside my Zeitgeist directory, considering only data from the last 6 months. Nice, huh? This is an experimental feature, and it doesn t work well when operating on big inputs, so it s usually better to use the find_related_uris_for_uris variant (which replaces the first query_templates parameter with a list of URIs). Advanced searching: the FTS extension Some people think prefix searches aren t good enough for them, and this is why the Zeitgeist engine ships by default with a FTS (Full Text Search) extension. Using the methods provided by this extension you can perform more advanced queries against subjects current_uri and text properties (unlike the name may suggest, the FTS extension doesn t index the content of the files, but just the information in the event). This is exposed as zeitgeist_index_search in libzeitgeist (the C library), but unfortunately isn t currently available in the Python API. If you still need it, you ll have to fallback to pretty much using the D-Bus interface (you still get reconnection support, though). Here s an example:
from zeitgeist.client import ZeitgeistClient
from zeitgeist.datamodel import *
 
zeitgeist = ZeitgeistClient()
index = zeitgeist._iface.get_extension('Index', 'index/activity')
 
query            = 'hello' # search query
time_range       = TimeRange.always()
event_templates  = []
offset           = 0
num_events       = 10
result_type      = 100 # magic number for "relevancy" (ResultType.* also work)
 
def on_reply(events, num_estimated_matches):
    print 'Got %d out of ~%d results.' % (len(events), num_estimated_matches)
    events = map(Event, events)
    for event in events:
        print ' - %s' % event.subjects[0].uri
 
def on_error(exception):
    print 'Error from FTS:', exception
 
index.Search(query, time_range, event_templates,
             offset, num_events, result_type,
             reply_handler=on_reply, error_handler=on_error)
 
# Start a mainloop
from gi.repository import GLib
GLib.MainLoop().run()
The most interesting thing here is the query parameter. Quoting from the C documentation:
The default boolean operator is AND. Thus the query foo bar will
be interpreted as foo AND bar. To exclude a term from the result
set prepend it with a minus sign - eg foo -bar. Phrase queries
can be done by double quoting the string "foo is a bar". You can
truncate terms by appending a *.
There are a few keys you can prefix to a term or phrase to search
within a specific set of metadata. They are used like key:value.
The keys name and title search strictly within the text field of
the event subjects. The key app searches within the application
name or description that is found in the actor attribute of the
events. Lastly you can use the site key to search within the
domain name of the subject URIs.
You can also control the results with the boolean operators AND
and OR and you may use brackets, ( and ), to control the operator
precedence.
Modifying the log So far we ve only queried Zeitgeist for information, let s get a bit more active. You can delete events from Zeitgeist with the following query:
from zeitgeist.client import ZeitgeistClient
 
zeitgeist = ZeitgeistClient()
 
def on_deleted(timerange):
    print 'Deleted events going from %s to %s' % (timerange[0], timerange[1])
 
event_ids = [50] # put the IDs of the events you want to delete here
 
zeitgeist.delete_events(event_ids, on_deleted)
 
# Start a mainloop
from gi.repository import GLib
GLib.MainLoop().run()
The confirmation callback will receive a timerange going from the first to the last event. If no events were deleted (because they didn t exist), you ll get (-1, -1).
And now for the interesting part. If your application involves resources (files, websites, contacts, etc.) of any sort, you ll probably want to let Zeitgeist know that you re using them. It s time that you write a data-source! We start by registering the data-source. Here we go:
import time
from gi.repository import GLib
from zeitgeist.client import ZeitgeistClient
from zeitgeist.datamodel import *
 
zeitgeist = ZeitgeistClient()
 
def on_status_changed_callback(enabled):
    """ This method will be called whenever someone enables or disables
        the data-source. """
    if enabled:
        print 'Data-source enabled and ready to send events!'
    else:
        print 'Data-source disabled; don\'t send event, they\'ll be ignored.'
 
def register():
    # Always use the same unique_id. Name and description can change
    # freely.
    unique_id = 'com.example.your.data.source'
    name = 'user visible name (may be translated)'
    description = 'user visible description (may be translated)'
 
    # Describe what sort of events will be inserted (optional)
    subject_template = Subject()
    subject_template.interpretation = Interpretation.PLAIN_TEXT_DOCUMENT
    subject_template.manifestation = Manifestation.FILE_DATA_OBJECT
    templates = []
    for interp in (Interpretation.ACCESS_EVENT, Interpretation.LEAVE_EVENT):
        event_template = Event()
        event_template.interpretation = interp
        event_template.manifestation = Manifestation.USER_ACTIVITY
        event_template.append_subject(subject_template)
        templates.append(event_template)
 
    zeitgeist.register_data_source(unique_id, name, description, templates,
                                   on_status_changed_callback)
Once that s done (and if it is enabled), we are free to send our events:
def log(title, uri, opened):
    subject = Subject.new_for_values(
        uri=uri,
        interpretation=Interpretation.PLAIN_TEXT_DOCUMENT,
        manifestation=Manifestation.FILE_DATA_OBJECT,
        origin=GLib.path_get_dirname(uri),
        mimetype='text/plain',
        text=title)
    event = Event.new_for_values(
        timestamp=time.time()*1000,
        manifestation=Manifestation.USER_ACTIVITY,
        actor='application://your_application_name.desktop',
        subjects=[subject])
    if opened:
        event.interpretation = Interpretation.ACCESS_EVENT
    else:
        event.interpretation = Interpretation.LEAVE_EVENT
 
    def on_id_received(event_ids):
        print 'Logged %s (%d) with event id %d.' % (title, opened, event_ids[0])
 
    zeitgeist.insert_events([event], on_id_received)
 
if __name__ == '__main__':
    register()
 
    log('test.txt', 'file:///tmp/test.txt', opened=True)
    log('another_file.txt', 'file:///tmp/another_file.txt', opened=True)
    log('another_file.txt', 'file:///tmp/another_file.txt', opened=False)
    log('test.txt', 'file:///tmp/test.txt', opened=False)
 
    # Start a mainloop
    GLib.MainLoop().run()
If you don t know what interpretation and manifestation your subject should have, you can use the following utility methods:
from zeitgeist.mimetypes import *
 
print get_interpretation_for_mimetype('text/plain')
print get_manifestation_for_uri('file:///tmp/test.txt')
Event better, with Zeitgeist 0.9 you can just leave the subject (but not event!) interpretation and manifestation fields empty, and they ll be guessed the same way as if you used those utility methods. Pro Tip: You can examine all registered data-sources and toggle whether they are enabled or not using the zeitgeist-data-sources-gtk.py tool. Conclusion Wow, I m impressed if you ve got this far. By now you should have quite a good idea on how to use the Zeitgeist API, and I m looking forward to seeing what you do with it in your next awesome project. If you have any problem with Zeitgeist, feel free to visit us on IRC (#zeitgeist on irc.freenode.net), or join our mailing list. We ll also be at GUADEC next week, so if you re there make sure to say hi! In case you missed them, here are some useful links:
No comments
Siegfried-Angel Gevatter Pujals, 2012. Permalink License Post tags: ,
flattr this!

20 July 2012

Joey Hess: are there any gotchas with enabling the -threaded GHC runtime?

That's a seemingly simple question I started asking various people two weeks ago. I didn't get many useful answers, but now I have experience doing it myself, and so here's a blog post brain dump. I have been trying to convert git-annex to use GHC's threaded runtime, for a variety of reasons. Naively adding the -threaded option resulted in a git-annex command that seemed to randomly freeze, but only sometimes (and, infuriatingly, never when I straced it), and a test suite that froze at a random point almost every time I ran it. Not a good result, and lacking any knowledge about gotchas with using the threaded runtime, I was at a loss for a long time (most of DebConf) about how to fix it. I now know of at least three classes of problems that enabling the threaded runtime can turn up in programs that have been developed using the non-threaded runtime. accessing a MVar after forkprocess can hang MissingH has some code similar to this, which works ok with the non-threaded runtime:
forkProcess $ do
    debugM "about to run some command"
    executeFile ...
In the above example, debugM accesses a MVar. Doing that after forkProcess can result in a MVar deadlock, as it tries to access a MVar value, that is, apparently, not accessible to the forked process. (Bug report with test case) So, using System.Cmd.Utils from MissingH is asking for trouble. I switched all my code to the newer and, apparently, threaded runtime safe System.Process. forkProcess is a massively bad idea Even when not accessing a MVar after forkProcess, it's very unsafe to use. It's easy to miss the warning attached to forkProcess, when the code seems to work. But with the threaded runtime, I've found that most any call to forkProcess will sometimes succeed, and sometimes freeze the whole program. This might only happen around 1 time in 1000. Then you'll find this warning and do a lot of head-scratching about what it really means:
forkProcess comes with a giant warning: since any other running threads are not copied into the child process, it's easy to go wrong: e.g. by accessing some shared resource that was held by another thread in the parent.
The hangs I saw could be due to laziness issues deferring code to run after the forkProcess that you'd expect to have run before it ... or who knows what else. It's not clear to me that it's possible to use forkProcess safely in Haskell code. I think it's notable that System.Process runs the whole fork/exec in C code instead. unsafe FFI calls block According to most of the documentation you'll find in eg, the Haskell wiki, Real World Haskell, etc, the only difference between the safe and unsafe imports in the FFI is that unsafe is faster, and shouldn't be used for C code that calls back into Haskell code. But the documentation is out of date. Actually, if you're using the FFI, and the foreign function can block, you need to use safe. When using unsafe, a blocking foreign function can block all threads of the program. In my case, I was using kqueue to wait for changes to files, and this indeed blocked my whole program when linked with -threaded. Marking it safe fixed this. The details are well described in this paper: http://community.haskell.org/~simonmar/papers/conc-ffi.pdf Somewhat surprisingly, this blocking only happens when using the threaded runtime. If you're using the non-threaded runtime with unsafe blocking FFI functions, your other pseudo-threads won't be blocked. This is because the non-threaded runtime has an SIGALARM timer that interrupts (most) blocking system calls. This leads to other troubles of its own (like needing to restart interrupted FFI functions, or blocking the other pseudo-threads from running if the C code ignores SIGALARM), but that's offtopic for this post. summary Converting a large Haskell code base from the default, non-threaded runtime to the threaded runtime can be quite tricky. None of the problems are the sort of thing that Haskell helps to manage either. I will be developing new programs using the threaded runtime from the beginning from now on. By the way, don't take this post to say that threading in Haskell sucks. I've really been enjoying writing threaded Haskell code. The control Haskell gives over isolation of state to threads, and the excellent and wide-ranging suite of thread communications data types (MVar, Chan, QSemN, TMVar, SampleVar, etc) have made developing a complex threaded program something I feel comfortable doing for the first time, in any language.

David Paleino: Editable QComboBox with QAbstractTableModel

Dear Lazyweb, I'm working on a personal PyQt4 project for my parents' office, and I need to autocomplete an editable QComboBox. I can't use a standard Qt model (I need to store python objects in it), so I subclassed QAbstractTableModel. Here it is, it's a fairly generic subclass of QAbstractTableModel, with variable number of columns:
class QGenericTableModel(QAbstractTableModel):
    def __init__(self, columns, parent=None, *args):
        super(QGenericTableModel, self).__init__(parent, *args)
        self.columns = columns
        self.headers =  
        self.table = []
    def rowCount(self, parent=QModelIndex()):
        return len(self.table)
    def columnCount(self, parent):
        return self.columns
    def row(self, row):
        return self.table[row]
    def setData(self, index, value, role):
        if index.isValid() and role == Qt.EditRole:
            row = index.row()
            t = self.table[row]
            if index.column() >= self.columns:
                return False
            if isinstance(value, QVariant):
                t[index.column()] = value.toString().simplified()
            else:
                t[index.column()] = value
            self.emit(SIGNAL('dataChanged'), index, index)
            return True
        return False
    def data(self, index, role = Qt.DisplayRole):
        if not index.isValid():
            return QVariant()
        if (index.row() >= len(self.table)) or (index.row() < 0):
            return QVariant()
        if role == Qt.DisplayRole:
            return QVariant(self.table[index.row()][index.column()])
        return QVariant()
    def insertRow(self, row, parent=QModelIndex()):
        self.insertRows(row, 1, parent)
    def insertRows(self, row, count, parent=QModelIndex()):
        self.beginInsertRows(parent, row, row+count-1)
        for i in xrange(count):
            self.table.insert(row, ['',]*self.columns)
        self.endInsertRows()
        return True
    def removeRow(self, row, parent=QModelIndex()):
        self.removeRows(row, 1, parent)
    def removeRows(self, row, count, parent=QModelIndex()):
        self.beginRemoveRows(parent, row, row+count-1)
        for i in reversed(xrange(count)):
            self.table.pop(row+i)
        self.endRemoveRows()
        return True
    def flags(self, index):
        if not index.isValid():
            return Qt.ItemIsEnabled
        return super(QGenericTableModel, self).flags(index)   Qt.ItemIsEditable
    def headerData(self, column, orientation, role = Qt.DisplayRole):
        if role != Qt.DisplayRole:
            return QVariant()
        if orientation == Qt.Horizontal:
            if column >= self.columns:
                return QVariant()
            return self.headers[column]
        return QVariant()
    def setHeaderData(self, column, orientation, value, role = Qt.EditRole):
        self.headers[column] = value
It might not be perfect, but it works, and it's all that matters at the moment (since it's my first serious PyQt4 project). Oh, well, it worked until I tried to use it with an editable QComboBox. And here's the app code (for the full working code, insert the above class right after the imports):
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
app = QApplication(sys.argv)
model = QStandardItemModel()
for i, word in enumerate(['saluton', 'gxis la revido', 'hello', 'goodbye']):
    item = QStandardItem(word)
    model.setItem(i, 0, item)
#model = QGenericTableModel(1)
#for i, word in enumerate(['saluton', 'gxis la revido', 'hello', 'goodbye']):
#    model.insertRow(i)
#    index = model.index(i, 0)
#    model.setData(index, word, Qt.EditRole)
combo = QComboBox()
filterModel = QSortFilterProxyModel(combo)
completer = QCompleter(combo)
filterModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
filterModel.setSourceModel(model)
filterModel.setFilterKeyColumn(0)
completer.setModel(filterModel)
completer.setCompletionColumn(0)
completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
combo.setEditable(True)
combo.setCompleter(completer)
combo.setModel(model)
combo.setModelColumn(0)
if combo.isEditable():
    app.connect(combo.lineEdit(), SIGNAL('textEdited(QString)'), filterModel.setFilterFixedString)
combo.setModel(model)
combo.setModelColumn(0)
combo.show()
sys.exit(app.exec_())
Now, try running it. It works well when I use a QStandardItemModel. Then, try decommenting the part where I use my subclassed model: it just doesn't work: clicking on any item makes the QComboBox not change its currentIndex, and this only happens if the combobox is editable. I suspect I'm forgetting to override some function in my model, but I don't know what exactly to override, and Google didn't help me. This is also backed up by the fact that the exact same behaviour shows up when, instead of a QComboBox, I try to autocomplete on a QLineEdit. So, dear readers: can anyone explain what is happening? Thanks in advance! UPDATE: it turned out that the culprit was the following bit inside data():
        if role == Qt.DisplayRole:
            return QVariant(self.table[index.row()][index.column()])
Adding also Qt.EditRole to the list of possible choices fixed the bug. YAY!

Neil McGovern: DebConf 2012 Nicaragua

After 4 flights, about 15,000 miles, a beach, some To a and cheese and lots of geeks, I'm able to update my Places I've Been map by adding Nicaragua to it. I was speaking at DebConf12, entitled Bits from the Release Team. In case you missed it, it's been recorded by the wonderful DebConf video team and is available in high and low quality. It was great to catch up with people again, and do quite a bit of work on Debian itself, mostly on the Wheezy release. I even found an hour to take a basic Spanish class, although I'm fairly sure I've forgotten most of it by now. As usual, there were a number of Collaborans present, and it was good to meet them. I suspect I'll be seeing a few more at GUADEC in a weeks time. My photos are available, should anyone be interested.

John Goerzen: A Linux-Based RFID Thing Finder

Sometimes I have things nicely organized. Power adapters for radios in one drawer, for cameras in the next. And sometimes not so much. Sometimes I m not sure if things are in the basement or the attic. It seems like technology should be able to help solve this problem, but as far as I can tell, no such solution exists yet. So I m planning to build one. Here s my general idea. Feedback, of course, is welcome. Each item to be tracked can have an RFID tag of some sort attached to it. These tags can be read by an RFID reader at a distance of somewhere between 1ft and 1m in typical conditions. The reader could then act as a proximity alert to an object being searched for a you re close beep, for instance. That helps, but is only part of the battle. It doesn t help if you don t even know which room to look in. So the second part of the plan is that the RFID reader is constantly talking to an object database. Besides the obvious association between RFID tag IDs and object descriptions, the database will also capture background reads of RFID tags. Logged with accurate timestamps, we can then conjecture that RFID reads that occurred within a few seconds of each other are probably physically nearby. If boxes have RFID tags on them, then I can probably get a reasonable idea, down to a box or two, of which box that elusive book is in. If I further put tags on certain immovable physical locations in the house, such as every few feet along shelving, then these will also be captured in the background and hopefully associated with objects nearby, giving a good physical idea of where things are. What s more, the simple act of looking for things using the RFID reader would help keep the proximity tables up-to-date, since it could of course log the RFID tags it sees on the way. The only discipline required to keep this info current is to periodically hover nearby storage areas when moving things around. I like this concept a lot. That doesn t mean it s necessarily simple. Implementation requirements Software side I m envisioning the software being split into two components. One would run on an embedded system with the RFID reader. Its job is simple transmitting of scanned RFIDs to the server, receiving instructions from the server, and generating a tone if it s in search mode and the item being searched is nearby. On the server lies a database. The database would contain descriptions for the objects that are tagged (so that the tag ID can be looked up). It would also contain the timestamped scan logs. I envision a simple CGI-based frontend to it that is mobile-friendly, so a laptop, phone, tablet, etc. could be the user interface for the thing saving the cost of a display and input device by reusing what most people already have. This is the part I feel most qualified to work on already. Hardware The first question is what kind of RFID tags to use. Optimizing for cost per tag, the 800/900MHz UHF tags (EPC gen 2) seem ideal. I have found them in costs approaching $0.10 per tag when bought in rolls of 500 or 1000 Avery RFID labels. That s reasonable. The RFID reader is the more complicated part. UHF RFID readers are a lot more costly than their HF or LF counterparts. So far, the cheapest solution I have found started with a post about an Arduino UHF reader. It used a SolidDigi UHF reader board with UART interface for $177. That same board is also available with a USB interface at the same cost. There is also the need for an antenna. There is a small 5dBi one rated 0-50cm for $8, or a $100 8dBi version rated 1-6m designed for wall mounting. They sell these in kit form that include power adapter, USB cables, etc. as well. The kit with the small antenna runs $192, including reader board. Next is the compute platform. A DreamPlug might work for this at $160, though both the cost and the power consumption (5VDC 3A) are high. A Raspberry Pi seems perfect, though for whatever reason seem to be backordered by months everywhere and don t include wifi. The Pi is $35, and the USB wifi is another $30, plus a cheap SD card, so we re around $80 of computing. The running total of the project, then, is at least $272. Add on provisions for batteries, some sort of case, etc. and we re probably past the $300 mark. That s a lot cheaper than the $1000 for handheld RFID readers. The final item is tags. Bulk tags can be found in the $100 to $200 range, making the total cost of the project $400 to $500. Higher than I d like, but providing some valuable experience building something. Risks There are a few major risks to the project. First among them is read distance. If it s not long enough, the usefulness of the project will be low. Fortunately, $100 gets a more high-gain antenna and I d only be out the $8 if the cheaper one doesn t pan out. But that s something of a cold comfort, as it s another, well, $100. Having a bunch of RFID hardware can be used for all sorts of other interesting things, though, so it could perhaps be reused or repurposed. Another risk is that RFID collisions wouldn t be handled as intelligently as I d like, meaning that the read range required to be useful would activate so many tags that collision algorithms break down. I don t know enough about RFID collision algorithms to know if this is a serious councern.

19 July 2012

Matthew Palmer: Melt your cores with Rake's multitask

Reading documentation does pay off. Browsing through the Rakefile format documentation for Rake, just now, I found mention of the multitask method which declares that all of that task s prerequisites can be executed in parallel. A comparison run:
$ rake clean; time rake build
real    0m7.116s
user    0m6.788s
sys     0m0.260s
$rake clean; time rake multibuild
real    0m3.820s
user    0m8.809s
sys     0m0.288s
This is a trivially small build I m doing, I must admit, but halving the build time (in this case at least) pays huge dividends in my perceived productivity. It really blows the dust out of my CPU cores, too, which tend to be woefully underutilised (being this is a quad-core laptop and all). So I say unto you all: go forth and multitask!

17 July 2012

Sylvain Le Gall: Coding by example, migrating ocsoap to OASIS and ocamlbuild

In my effort to automate most of the stuff needed to release, I am working on a SOAP to OCaml converter. Richard W.M. Jones has kindly allowed me to take-over its project "ocsoap". The project is now located here and I am working on making it compatible with FusionForge SOAP. My first step was to make it compatible with OASIS and ocamlbuild. The project is Makefile based and needs some extra care to make it ocamlbuild based. The oasis-fication itself was a piece of cake. Just describe the dependencies of the project and make some extra choice like compiling the camlp5 extension to a .cma rather than a .cmo. You can see the _oasis and myocamlbuild.ml results, compared to the initial Makefile. The _oasis is simple:
OASISFormat:  0.3
Name:         ocsoap
Version:      0.7.1
Synopsis:     SOAP converter to OCaml code
Authors:      Richard W.M. Jones, Sylvain Le Gall
License:      LGPL-2.1 with OCaml linking exception
Plugins:      DevFiles (0.3), META (0.3), 
              StdFiles (0.3)
BuildTools:   ocamlbuild,
              cduce
BuildDepends: dynlink,
              pxp-lex-utf8,
              pxp-engine,
              netclient,
              cduce,
              extlib,
              calendar,
              pcre
Library ocsoap
  Path:       src
  Modules:    OCSoap
Library pa_ocsoapclientstubs
  Path:          src
  Modules:       Pa_ocsoapclientstubs
  BuildTools:    camlp5o
  BuildDepends:  camlp5
  FindlibParent: ocsoap
  FindlibName:   syntax
  CompiledObject: byte
  
Executable wsdl_validate
  Path:       src
  MainIs:     wsdl_validate.ml
  
Executable wsdltointf
  Path:       src
  MainIs:     wsdltointf.ml
Executable adwords_test1
  Path: examples/adwords
  BuildDepends: ocsoap
  MainIs: test1.ml
  Build$: flag(tests)
  Install: false
  BuildTools: camlp5o
  BuildDepends: ocsoap.syntax
Executable adwords_test2 
  Path: examples/adwords
  BuildDepends: ocsoap
  MainIs: test2.ml
  Build$: flag(tests)
  Install: false
  BuildTools: camlp5o
  BuildDepends: ocsoap.syntax
Executable adwords_examples 
  Path: examples/adwords
  BuildDepends: ocsoap
  MainIs: example.ml
  Build$: flag(tests)
  Install: false
  BuildTools: camlp5o
  BuildDepends: ocsoap.syntax
Document "api-ocsoap"
  Title: API reference of OCSoap
  Type: ocamlbuild (0.3)
  BuildTools+: ocamldoc
  XOCamlbuildLibraries: ocsoap
  XOCamlbuildPath:      src/
Most of it was generated using oasis quickstart, that helps you to write _oasis. This part was not tricky and mostly a translation of what the previous Makefile was explaining, in its language. Now comes the tricky part, translating Makefile rules into ocamlbuild rules. This part is more tricky because the general principle behind Makefile and ocamlbuild are not exactly the same. Let's start with a simple rule: compiling a CDuce .cd file into a .cdo. Here is the Makefile rule:
%.cdo: %.cd
        $(CDUCE) --compile $<
In the oasis-fication, we have added an extra complexity, because we move the source to src, which needs extra flags. Here is the ocamlbuild rule:
rule "cduce: %.cd -> %.cdo"
  ~prod:"%.cdo"
  ~dep:"%.cd"
  begin
    fun env build ->
      Cmd(S[cduce;
            T(tags_of_pathname (env "%.cd")++"cduce"++"compile");
            A"--compile";
            A"-I"; P(Filename.dirname (env "%.cd"));
            P (env "%.cd")])
  end
;;
It is a little bit more complex, lets explain it. The function rule create a rule with a name and a function that execute it. The labels ~prod and ~dep are the right and left parts of %.cdo: %.cd of the Makefile. When we call env "%cd", it replaces the % by the matching part of ~prod and ~dep. Next we return a Rule.action. The rule in this case is a command Cmd. This rules use a sequence S, which are the command line itself. The sequence is made of smaller pieces that can be a placeholder for tag content (T + what will trigger it), atom (A, typically command line option), and filename (P). For a file src/foo.ml, the command generated will
cduce $(TAG) --compile -I src src/foo.ml
where $(TAG) will be replaced by the content of flag ["file:src/foo.ml"; "cduce"; "compile"] content. But also by the content of flag ["cduce"; "compile"] content, the flag just need to be subset. For example if you want to add --verbose to the command line, just add flag ["cduce"; "compile"] (S[A"--verbose"]);; somewhere in myocamlbuild.ml. Next rules, we need to compile %.cdo to %.cmo. It is trickier because in this case, we want to have %.cmi eventually built before. It is not require to build it before, if there is no %.mli file. Here is the code to do that:
let cduce_mkstubs includes =
  Quote (S([cduce;
            S(List.map
                (fun fn -> S[A"-I"; P fn])
                includes);
            A"--mlstub"]));;
(* cduce < 0.3.9:  cdo2ml -static *) 
let cduce_compile_args env =
  [
    A"-c"; A"-package"; A"cduce";
    A"-pp"; cduce_mkstubs [Filename.dirname (env "%.cmi")];
    A"-I"; P(Filename.dirname (env "%.cmi"));
    A"-impl"; P(env "%.cdo")
  ]
;;
let cduce_prepare_compile env build =
  List.iter
    (function
         Outcome.Bad _ ->
           (* Fail but it just means that the .cmi will be generated 
            * during compilation.
            *)
           ()
         Outcome.Good _ ->
           ())
    (build [[env "%.cmi"]])
;;
rule "cduce: %.cdo -> %.cmo"
  ~prod:"%.cmo"
  ~dep:"%.cdo"
  begin
    fun env build ->
      cduce_prepare_compile env build;
      Cmd(S(
        [ocamlfind; ocamlc;
         T(tags_of_pathname (env "%.cdo")
           ++"cduce"++"ocamlc"++"compile"++"byte")]
        @ (cduce_compile_args env)))
  end
;;
The difference with the former rules, is that we call cduce_prepare_compile which in turns call (build [[env "%.cmi"]]). The call to build will ask ocamlbuild to compile src/foo.cmi, but we don't care about the result, i.e. in case of Outcome.Bad exn we don't fail. This way we don't stop the build process that will continue and produce a .cmo and .cmi just out of the .cdo. The .cdo itself is compiled as a .ml file with ocamlfind ocamlc except that we apply cduce --mlstub as a preprocessor A"-pp"; Quote(S[cduce; ..; A"--mlstub"]). The last rule that I will comment is the one that transform a .intf into a .ml. This rule is particular because it is totally different than the pieces of code of the original Makefile. Here are the couple of rules that was needed to translate .intf:
examples/adwords/%Service.cmx: examples/adwords/%Service.intf                                                       
   ocamlfind ocamlopt $(OCAMLOPTFLAGS) -c \                                                                          
     -pp "camlp5o ./pa_ocsoapclientstubs.cmo -impl" -c -impl $<
.depend: $(wildcard *.mli) $(wildcard *.ml) \
  $(wildcard examples/adwords/*.mli) $(wildcard examples/adwords/*.ml)
  $(OCAMLDEP) $^ > .depend
  for f in examples/adwords/*.intf; do \
    $(OCAMLDEP) \
    -pp "camlp5o ./pa_ocsoapclientstubs.cmo pr_o.cmo -impl" $$f \
    >> .depend; \
  done
Here is the ocamlbuild rule:
rule "ocsoap: %.intf -> %.ml"
  ~prod:"%.ml"
  ~deps:(if !ocsoap_dev then
           ["%.intf"; !pa_ocsoapclientstubs]
         else
           ["%.intf"])
  begin
    fun env build ->
      Cmd(S[Px !camlp5o; P !pa_ocsoapclientstubs; P "pr_o.cmo"; A"-impl";
            P(env "%.intf"); A"-o"; P(env "%.ml")])
  end
;;
Here we decided to translate directly the .intf into a .ml file. The good thing about ocamlbuild is that it has a powerfull dynamicy dependencies scheme. So here you will generate the .ml file, which in turns will generate a .ml.depends and then will be compiled the standard way. In the makefile, you need to compute the .depends file using a different process that will do everything before the compilation even starts (in fact, before the inclusion of the .depends). We also use the trick to use an OCaml printer (pr_o.cmo) with camlp5o, that will directly output the standard .ml file. Don't hesitate to post comment if you have question about OASIS and ocamlbuild Enjoy.

16 July 2012

R&#233;mi Vanicat: Sofware as service

Someone recently give me a link to a web picasa photo album. I wanted to download all the photo in good resolution, but the only official way to download it is by using the picasa proprietary software. Not only I'm not thrilled to install yet another unfree software, but Google refuse me to download it because "Picasa is not currently available for your operating system". Another solution exist, but need some research: Some time, its great to be able to beat limitation of unfree software thanks to free software.

15 July 2012

Jordi Mallach: Season of change

It feels like I'm sitting in a roller-coaster wagon. There's probably too much change going on for me to assimilate naturally. In particular: I just wrapped up (well, mostly) one of the toughest Uni semestres. I had to deal with lots of very time consuming assignments, and then the usual round of final exams. Even if this semester I got the best marks in my journey (or shall we say Via Crucis) through University, I still managed to fail one exam, for the Advanced Networks subject, which is quite annoying, given I got high marks (even the highest in one case) in other subjects I really don't master at all. In any case, this is the end of the pain. The only thing that's left is just one exam and a project based on GNU/Linux technologies which will basically mean formatting for prettyness the sysadmin docs we've been collecting at the office during the last few years. This effort will be nothing to what I've been doing during the past 18 months, so I'm really relieved to have it past me already. Getting rid of studies comes just two weeks before a big change in my professional career. Friday was my last day at the Institut Tecnol gic d'Inform tica, after five and a half fantastic years working with awesome people in a very friendly atmosphere. I've learned a great deal, and taking this decision wasn't easy at all. I leave lots of good friends behind, people I really love, and tomorrow will be difficult to not have them around me. I wish my ITI ex-workmates the best of luck in these difficult times for everyone in Spain and specially in the Valencian Country with the massive cuts going on. I feel the timing for this jump couldn't be better. Tomorrow, when I get ready to go for work, I won't be leaving home at all, instead I will just sit where I am right now, at home, and log into some corporate IRC server. Tomorrow I'll be joining Collabora, and I'm a mix of excited, curious and happy about this incredible opportunity. Thanks to Sjoerd for nags, I might not be writing this if it wasn't for you!

Collabora When I was first approached about this, I thought Collabora was a small company. But as I looked more into it, I discovered that's not longer the case, there's many more people than I imagined working there (here!), and was delighted to see I knew many of them, and many other are well known members of the major Free Sofware communities. I'll be joining the sysadmin team to work closely with Jo Shields. See you tomorrow, folks. :) This opportunity to work from home is godsend, given the third bit of change that'll be happening soon: sometime in late September, Maria and I should join the ranks of first-time parents, following the baby boom wave surrounding us. While you can imagine we're really happy about this, we're also freaking out because this is going to happen in just two months and a half, and weeks go by really fast lately. So yeah, being able to be at home with this really small baby will be a big bonus for the incredible experience we're about to enjoy. We've been both busy with other stuff, but during the summer we should be focusing on preparing the baby's arrival. There's a whole lot to do! Expect my Debian and other Free Software activities to get a hit, of course. :) If I am normally sleep-deprived, this is going to be the next level.

Mike Hommey: Comment spam

Three weeks ago, I slightly modified the comment system on this blog for an experiment. This blog is a standard wordpress installation. Comments are normally directed to the wp-comments-post.php script by the HTML form. What I did is: During the past three weeks, on this blog, there were 7170 comments, 8 of which were actual comments. 7162 were spam (~99.9%). This means a large portion of spammers didn t care about actually checking the comment forms and used the standard wordpress url, and another large portion don t run javascript on their bots, although a very few do.

Vasudev Kamath: Note to Self: Deleting Remote Branches and Tags in Git

Its been a while I'm using Git as a version control system for my various work whether it is FOSS related or my professional work. Every time I stumble into a case where I need to delete an accidentally pushed branch or tag on remote and every time I forget how I did it last time. Today I did it again and I had to search it again to find a method. So here it goes this is a note to myself so I won't forget it next time Deleting a remote branch
git push origin :branchname
Deleting a remote tag
git push origin :refs/tags/tagname
Where origin can be replaced by proper remote if you are using multiple remote for mirroring purpose. If you want to delete locally use following code
git branch -d branchname
git tag -d tagname
Note: To delete tag remotes you need to delete it locally first and then push it. That's it hope it helps others too :-)

Steve Langasek: For those who care about my foot

Sitting in the airport during a layover on my way back from DebConf is one of the few times of year that time and motivation come together and routinely lead me to blog. Each year's DebConf has been special in its own way. This year at DebConf 12, in addition to the usual routine of catching up with all my far-away Debian friends, and of making new Debian friends in a country I've never been before, I got a special treat: my first opportunity to experience the practice of medicine in a foreign country. Last Saturday after arriving in Managua, I discovered that the very pretty paving stones making up the hotel walkway were also very slippery when wet, and the thin rubber strips that had been helpfully put in place to make them less slippery did not measurably succeed in doing so. So as it was raining that afternoon (as it does nearly every afternoon this time of year in Nicaragua), I slipped, and made the critical error of not falling. Instead, I stopped my slide - as it happens, by using the big toe of my left foot as if it were a roller skate brake. I don't recommend this, particularly if, as in my case, you already had it on your todo list to consult a podiatrist after DebConf regarding an ongoing feeling of weakness in precisely that part of precisely that foot. So while everything seemed fine the next day, Monday I woke up early with excruciating pain in my foot which, having forgotten about the incident two days prior, seemed to come from nowhere. After favoring the foot for another day and trying to keep the pain at bay using just OTC drugs, by Tuesday my foot was quite swollen and a source of concern. So I visited the clinic on the university campus, where the doctor diagnosed me with a dropped arch plus cellulitis due to bacteria entering through some invisible cut. Conclusion: university campus clinics are equally useless the world over. But at least in addition to pointlessly prescribing antibiotics, the doctor also prescribed some anti-inflammatory drugs to help bring the swelling down. Nevertheless, my questionable mobility kept both me and Patty from attending the day trip on Wednesday, which was a real shame as I had been keen to climb a volcano that day with my fellow Debian Developers (which in the end didn't happen for anyone, but that's a story for someone else's blog). Instead I took it easy for the day at the hotel, together with a few others who had opted not to attend the day trip, catching up on email and other work and hoping matters would improve. But by the end of the day the swelling was as bad as ever, if not worse, and I had no idea what was going on. Was it broken? Was something dislocated? And so I decided that the following day I would go to the hospital. The nearest hospital, as given in the DebConf local information page, is the Hospital Militar. It's a strange sort of thing as an American who remembers the 1980s to be seeking medical care from a facility operated by the Nicaraguan military; but accompanied by Fitoria, my native escort from the local team I had nothing to worry about; and besides, as a frequent international traveler I long ago learned the secret truth that people are people everywhere, no matter what flag they were born under. The highlight of the hospital visit was definitely when a member of the hospital staff, in full military uniform, shooed me towards a chair instead of standing to talk to her because "every patient has the right to sit, no matter what country they're in". After six x-rays and a doctor's consult, the conclusion was that it's probably not a broken bone and that it will probably clear up in just a couple of days with an even-better anti-inflammatory prescription and taking care of it by not walking on it. And indeed, my foot was already feeling better by Friday and aside from a little tenderness is now just about 100%. There are plenty of other entries on Planet Debian talking about the conference itself, and I don't think I have much to add to what's already been said there. But as for Nicaragua, I can conclude that it must be one of the best countries in the world to break your foot in - between the help of the hotel staff, the top-notch medical professionals, and above all the support of the DebConf local team (with a very big thank you again to Fitoria!), this is one brand of socialized medicine I can appreciate.

14 July 2012

Christian Perrier: Cool prompt for git users with bash

Here's what I found out during the "Packaging with git" talk at Debconf12. There are certainly cool enhancements. Feel free to share on Planet.
bubulle@sesostris:~ $ cat .bashrc
# http://lukasrieder.com/2009/07/14/extend-your-bash-ps1.html
parse_git_branch()  
  git branch 2> /dev/null   sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/'
 
# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
xterm-color xterm screen)
    PS1='$ debian_chroot:+($debian_chroot) \[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\[\033[01;33m\]$(parse_git_branch)\[\033[00m\] \$ '
    ;;
*)
    PS1='$ debian_chroot:+($debian_chroot) \u@\h:\w$(parse_git_branch)\$ '
    ;;
esac

Next.

Previous.