ssh-agent and LD_LIBRARY_PATH

Quick post about a quirk that cost me a few hours in debugging: trying to understand why the LD_LIBRARY_PATH environmental variable was getting unset in my tmux sessions, lunched through ssh-agent (basically, by running ssh-agent tmux).

Long story short, it has nothing to do with tmux — and all with ssh-agent.

According to this page,

Since ssh-agent(1) has the SGID bit set, it ignores LD_LIBRARY_PATH and does not propagate the variable to its children.

There are some workarounds on that page already, but the one that’s not mentioned there and did the trick for me was to run eval `ssh-agent` and, subsequently, tmux.

On StackOverflow, off-topic questions and code reviews

I was reviewing the “first posts” queue on StackOverflow yesterday and one of questions I had to review was not particularly clear nor very well written.

Basically, the poster had a script which was looping and interacting with the user and her question was (I think) how to exit this infinite loop at some point.

The question was unclear, and the code wasn’t particularly elegant, not really what can be regarded as a Minimal Working Example, yet it was actually running. Needless to say, by the time I was ready to make some comments and edits, the post had already been closed as “off-topic”.

In my mind, that’s a bit of a shame. I understand it wasn’t the best question in the world but, picture this: there’s someone who’s trying to write some program, she manages to cobble together some code (we all started out like that, come on), she actually makes it run, stumble upon a blocker, turns to the wisdom of the (geek) crowd for help… only to get told to ask a better question. Like, “Yeah yeah, sorry, got no time, can’t understand what you want, go and play hide and seek on the Autobahn”.

I don’t know, it feels wrong. I mean, people will have to start off at some point, they can make mistakes, right? Which is why I flagged the post and asked it to move it to the Code Review StackExchange — awesome for this particular type of questions.

Got no response, most likely my request was ignored, no big deal. Anyhow, I edited the post today and asked (again) for it to be moved to CR. I’m not sure it’ll ever be re-opened and moved, so what I thought of doing is give my view of that code here on my blog and offering solution. It can (of course) still be improved, but it’s a start.

Data structures

#Phones
p = ["BPCM", "BPSH", "RPSS", "RPLL", "YPLL", "YPLL"]
p_desc = ["Compact", "Clam Shell", "RoboPhone - 5-inch screen and 64 GB memory", "RoboPhone - 6-inch screen and 256 GB memory", "Y-Phone Standard – 6-inch screen and 64GB memory" , "Y-Phone Deluxe – 6-inch screen and 256GB memory", ""]
p_price = [29.99, 49.99, 199.99, 499.99, 549.99, 649.99]

#Tablets
t = ["RTMS", "RTLM", "YTLM", "YTLL"]
t_desc = ["RoboTab – 8-inch screen and 64GB memory", "RoboTab – 10-inch screen and 128GB memory", "Y-tab Standard - 10-inch screen and 128GB memory", "Y-tab Deluxe - 10-inch screen and 256GB memory"]
t_price = [149.99, 299.99, 499.99, 599.99]

The snippet above shows something interesting. for phones you have a list of product codes, followed by a list of descriptions, followed by a list of prices. The poster commented she wasn’t able to use 2-D array, presumably because she wanted to have a list of lists, the latter being something like “code, description, price”.

Since the structure is followed by all the items in the code, a better solution would be to collect these properties into an object.

class Item:
    """Class representing a generic item, having a code, description and a price"""

    def __init__(self, code, description, price):
        self.code = code
        self.description = description
        self.price = price

    def __str__(self):
        return "{} ({}) - price: {}".format(self.description, self.code, self.price)

    def __repr__(self):
        return "Item({}, {}, {})".format(self.code, self.description, self.price)

So that you can define a phone or a tablet as Item("BPCM", "Compact", Decimal("29.99"))

An additional improvement we can do is the use of something better than lists for collecting our items. I’d suggest a simplified mapping where items are stored by code:

class ItemCollection:
    """Collection of Items, stored by code"""

    def __init__(self, *items):
        self.collection = {item.code: item for item in items}

    def __getitem__(self, code):
        return self.collection

    def __iter__(self):
        return iter(self.collection.items())

    def codes(self):
        return self.collection.keys()

Flow control

running = True

while running == True:
  print ("Do you want a phone or a tablet")

Couple of things we can do better here. First, checking wether a value is True (or False) should be done through the identity operator (is) not the equality one (==). This is because there is only one True object. Second, we can get rid of this check altogether, as we can simply break out of the loop at some point, depending on what the user of the script decides.

This would become something like:

while True:
    ...
    add_another = present_choices("Add another device? ", {"Y", "N"}).upper()
    if add_another != "Y":
        break

Input/Output

 if phone_tablet == ("phone"):
    print("\nCode -")
    print (p)
    print("\nDescription -")
    print(p_desc)
    print("\nprice -")
    print(p_price, "\n")
    p_choice = input ("Enter the code of the phone \n")
    while p_choice not in p:
      print("Unvalid code, try again")
      p_choice = input("Enter the code again ")
    pass

Lots of prints going on here. We can probably tidy them up. Also there's a superfluous pass statement that can be removed and some typos (Unvalid). What we can notice is also that the prints are repeated in a similar fashion throughout the code, though they're not exactly the same. We could probably reuse them and create a function. Something like:

def show_items(items, intro_text=None, outro_text=None):
    """Print to the standard output the items in the collection

    :param items: ItemCollection instance
    :param intro_text: (optional) text to prepend to the output
    :param outro_text: (optional) text to append to the output
    """
    line_format = "{:<4} | {:<50} | {:<8}"
    header = line_format.format("Code", "Description", "Cost")
    separator = "-" * len(header)

    if intro_text:
        print(intro_text)
        print()

    print()
    print(header)
    print(separator)
    for _, item in items:
        print(line_format.format(item.code, item.description, item.price))
    print(separator)

    if outro_text:
        print(outro_text)
        print()

It could be made even more generic (notice that it has knowledge of "Code", "Description" and "Cost") and, in general, better (fewer print()s) but it's a valid starting point.

Getting the input is also pretty much the same operation, regardless of the type of the device (enter something, if not valid try again, otherwise continue). Here, too, we can reuse our code if we put it into a function.

def present_choices(prompt, valid_choices, error_prompt="Invalid choice. ") -> str:
    """present_choices: ask the user for input

    :param prompt: string to present to the user (e.g. "Type 'phone' or 'tablet': ")
    :param valid_choices: collection of inputs that can be accepted
    :param error_prompt: string to present to the user in case she fails to give a valid choice,
        defaults to "Invalid choice. ".

    :return: string representing the choice made
    """
    choice = input(prompt)
    while choice not in valid_choices:
        choice = input(f"{error_prompt}{prompt}")

    return choice

General comments

Other things that can be improved in general:

  • Respect PEP 8 for better maintainability - it always pays off.
  • Write unit tests to fend off regressions - no quibbling!
  • Type hints could also help. I'm guilty myself here, coming from a background almost exclusively made of Py2.
  • Encoding directive at the beginning of the script. This is another thing that caught me off: the original code contains some non-ASCII characters and Python will complain when trying to parse it unless you add something like -*- coding: utf-8 -*- at the top of the script.
  • The use of f-strings, reducing the amount of code needed to populate strings.
  • Anything else I missed? Anything that's not quite right? Let me know in a command: I'll be happy to know (how would anyone learn without feedback?!)

My attempt at a solution would look like this:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from decimal import Decimal


class Item:
    """Class representing a generic item, having a code, description and a price"""

    def __init__(self, code, description, price):
        self.code = code
        self.description = description
        self.price = price

    def __str__(self):
        return "{} ({}) - price: {}".format(self.description, self.code, self.price)

    def __repr__(self):
        return "Item({}, {}, {})".format(self.code, self.description, self.price)


class ItemCollection:
    """Collection of Items, stored by code"""

    def __init__(self, *items):
        self.collection = {item.code: item for item in items}

    def __getitem__(self, code):
        return self.collection

    def __iter__(self):
        return iter(self.collection.items())

    def codes(self):
        return self.collection.keys()


phones = ItemCollection(
    Item("BPCM", "Compact", Decimal("29.99")),
    Item("BPSH", "Clam Shell", Decimal("49.99")),
    Item("RPSS", "RoboPhone - 5-inch screen and 64 GB memory", Decimal("199.99")),
    Item("RPLL", "RoboPhone - 6-inch screen and 256 GB memory", Decimal("499.99")),
    Item("YPLL", "Y-Phone Standard – 6-inch screen and 64GB memory", Decimal("549.99")),
    Item("YPLL", "Y-Phone Deluxe – 6-inch screen and 256GB memory", Decimal("649.99")))


tablets = ItemCollection(
    Item("RTMS", "RoboTab – 8-inch screen and 64GB memory", Decimal("149.99")),
    Item("RTLM", "RoboTab – 10-inch screen and 128GB memory", Decimal("299.99")),
    Item("YTLM", "Y-tab Standard - 10-inch screen and 128GB memory", Decimal("499.99")),
    Item("YTLL", "Y-tab Deluxe - 10-inch screen and 256GB memory", Decimal("599.99")))

sim_cards = ItemCollection(
    Item("SMNO", "SIM Free (no SIM card purchased)", Decimal("0.00")),
    Item("SMPG", "Pay As You Go (SIM card purchased)",  Decimal("9.99")))

cases = ItemCollection(
    Item("CSST", "Standard", Decimal("0.00")),
    Item("CSLX", "Luxury", Decimal("50.00")))

chargers = ItemCollection(
    Item("CGCR", "Car", Decimal("19.99")),
    Item("CGHM", "Home", Decimal("15.99")),
    Item("CGNO", "No charger", Decimal("0.00")))


total_price = Decimal("0")

def show_items(items, intro_text=None, outro_text=None):
    """Print to the standard output the items in the collection

    :param items: ItemCollection instance
    :param intro_text: (optional) text to prepend to the output
    :param outro_text: (optional) text to append to the output
    """
    line_format = "{:<4} | {:<50} | {:<8}"
    header = line_format.format("Code", "Description", "Cost")
    separator = "-" * len(header)

    if intro_text:
        print(intro_text)
        print()

    print()
    print(header)
    print(separator)
    for _, item in items:
        print(line_format.format(item.code, item.description, item.price))
    print(separator)

    if outro_text:
        print(outro_text)
        print()


def present_choices(prompt, valid_choices, error_prompt="Invalid choice. ") -> str:
    """present_choices: ask the user for input

    :param prompt: string to present to the user (e.g. "Type 'phone' or 'tablet': ")
    :param valid_choices: collection of inputs that can be accepted
    :param error_prompt: string to present to the user in case she fails to give a valid choice,
        defaults to "Invalid choice. ".

    :return: string representing the choice made
    """
    choice = input(prompt)
    while choice not in valid_choices:
        choice = input(f"{error_prompt}{prompt}")

    return choice


while True:

    device_type = present_choices("Which type of device would you like? Type 'phone' or 'tablet': ",
                                  {"phone", "tablet"})

    devices = phones if device_type == "phone" else tablets
    show_items(devices)
    device_code = present_choices("Enter the code of the device of your choice: ", devices.codes())

    show_items(sim_cards)
    sim_card_code = present_choices("Enter the code of the SIM card of your choice: ", sim_cards.codes())

    show_items(cases)
    case_code = present_choices("Enter the code of the case of your choice: ", cases.codes())

    show_items(chargers)
    charger_code = present_choices("Enter the code of the case of your choice: ", chargers.codes())

    selection = ItemCollection(
        devices[device_code],
        sim_cards[sim_card_code],
        cases[case_code],
        chargers[charger_code])

    price = sum(i.price for (_, i) in selection)
    total_price += price
    show_items(selection, intro_text="Summary of your selection", outro_text=f"The total price is {price}")

    add_another = present_choices("Add another device? ", {"Y", "N"}).upper()
    if add_another != "Y":
        break


print(f"The total price is {total_price}")

Who knows, maybe the poster will never stumble upon this blog post.

Or maybe she will, in which case I'd tell her "Hang on, you'll soon be on the other side of the fence, doing code reviews for those who have just started!".

Customising tool bar and menu items in Evolution

Ever wondered how to add or remove buttons from the toolbar in Evolution? Me too.

It never annoyed me so much as to prompt for a fix but today I took the time to duck duck for a solution and the answer was just a couple of links away. The Evolution team decided not to include a graphical editor to move around buttons and edit toolbars and menus in general but, at the same time, did want to allow users willing to personalise their email client to be able to do so.

Turns out, the client’s views are defined in .ui files, usually relegated in /usr/share/evolution/ui/ (or $PREFIX/share/evolution/ui/ for non-standard installations).

The quite readable XML files in those folders will dictate how buttons and menus have to appear when you fire up your client. To override them, just copy the relevant UI files over to your local config folder – usually $HOME/.config/evolution/ui/ and edit them as necessary.

Save, restart the client and, lo and behold, more (or less) buttons in your toolbar!

This is actually documented in the app’s help, but, unfortunately, I found out about it only after finding the commit explaining the overriding mechanism following a link in a thread by another user trying to accomplish the same thing. Oh, well!

First steps in Rust

Photo by Jimmy Ofisia on Unsplash

As I’m going through the fabulous Rust tutorial, I’ve decided to jot down some of the concepts I stumble upon, mainly for my benefit. It loosely follows the “learn X in Y minutes” approach, which I don’t find particularly useful if I’m reading something that others wrote, but do find valuable when I do it myself.

You can try out each example by surrounding it with fn main() { and } and pasting it into the “Code” box at Try It Online.

Without further ado, meet “hello, world”.

fn main() {
    println!("Hello, world!");
}

Now I’ll assume that everything is inside fn main(), unless otherwise stated, so as to reduce boilerplate coding.

Immutable variables and shadowing

A variable can be shadowed and the shadowed value is lost. Notice you must use let. If you don’t you’ll get a compile-time error: cannot assign twice to immutable variable five. We are effectively creating a new variable with the same name.

// non mutable vars
let five = 5;
println!("{}", five);

// shadowing
let five = 1;
println!("{}", five);

This will print 5 and 1.

Type Hinting

Add the type after the name of the variable, separated by a colon. In the example below, if you don’t specify the type, you’ll get a compile-time error: cannot infer type - considering giving `anint` a type. This is because parse() is generic enough that can cause problems to the type inference system.

As an alternative, you can use the “turbofish” syntax (::<>) to help the inference algorithm understand which type you want to parse into.

// type hinting
let anint: i32 = "123".parse().expect("not right");
let anint = "321".parse::<i32>().expect("not right");
println!("{}", anint);

This will print 123. I’ll get to the expect() part later.

Mutable variables

Use the mut keyword to define them. Assigning new values happens without the keyword. In place assignments also work.

// mutable variables
let mut notfive = 2;
println!("{}", notfive);

notfive = 3;
println!("{}", notfive);

notfive += 1;
println!("{}", notfive);

This prints 2, 3, 4.

Overflow

Overflow checking happens in debug mode and, if one happens, the program will panic with an error message along these lines: thread 'main' panicked at 'attempt to add with overflow', src/main.rs:10:5.

In release mode, overflows are not checked and, instead, are specified to wrap as two’s complement.

// overflow
let mut over: i8 = 127;
over += 1;
println!("{}", over);

This block will never get to the print in debug mode, but will print -128 in release mode.

Consts

Not really much to say here.

// consts
const XY: u32 = 22;

Tuples

Tuples are ordered, fixed-length collections of items that can be of different type.
As usual, you can explicitly add type hinting.

To access items you can use the dot notation, where after the dot you specify the index of the element you want to access (for instance, .0 to access the first element).

// tuples
let atup: (u8, i16, f32) = (1, 2, 3.2);
println!("{}", atup.0);

let mut btup = (1, 2, 'y', "ah");
btup.1 += 3;
println!("{}", btup.1);

This will print 1 and 5.

“Tuple destructuring” is for Rust what “tuple unpacking” is for Python. Of course, parentheses are not optional in Rust.

You can destructure a tuple to a mutable variable, but that variable will hold the value, not the reference of the item in the tuple. Therefore, even if you change the variable, the item in the tuple won’t change.

let ctup = ("something", 2, 'x', 3.5);

// tuple destructuring
let (_, _, the_x, _) = ctup;
println!("{}", the_x);

let (_, mut the_two, _, _) = ctup;
the_two += 100;
println!("{}", the_two);
println!("{}", ctup.1);

This prints x, 102, and 2.

Arrays

The syntax – if you don’t specify type hinting – is similar to other languages (such as C, for instance). If you do specify the types, you’ll also have to specify the size in a type : size format.

Printing of the whole array does not work out of the box invoking the println! macro with the usual {} formatting option. However, it works if you use {:#?} – which tells the formatter to use the “alternate” form of printing and, among the alternate forms, the debug pretty printing form.

// arrays
let arr = [1, 5, 2, 4, 3];
println!("{:#?}", arr);

let another_arr: [i8; 3] = [6, 3, 5];
println!("{}", another_arr[0]);

The output is [1, 5, 2, 4, 3] (vertically spaced) and 6.

Next time, functions.

From Vim to Spacemacs

I’m very fond of Vim. It’s lean, it’s everywhere (if not, there’s vi anyway), it does the job. I also love Neovim and I’ve set it up with some plugins to ease Python development, such as vim-fugitive, a Git wrapper, and jedi-vim, autocompletion and static analysis for Python.
Undeniably, there are quite a few resources on the interwebs on how to create your “perfect” IDE out of Vim.

All nice and lovely, and it gets the job down but… sometimes some plugins let me down. They get confused, cannot find where a variable has been defined, autocompletion stops working properly and undefined vars are just not signalled. At this point I’m forced to close all my tabs and start again.

Even though it’s bearable, I got lured by some colleagues of mine to the dark side of Spacemacs, and I have decided to give that a try. Here are the bits I tweaked, to make Spacemacs feel more comfortable (to me).

Disclaimer

The following applies to Spacemacs on Linux.
I’m still learning my ropes on Spacemacs, so it might well be that some tweaks can be done in a better way or are not effective in all edge cases. Any feedback is, of course, most welcome!

Mouse, be gone

I only use my editors in the terminal, and I tend to use the mouse the same way I use it in other terminals: selecting text copies it, the middle button of the mouse pastes it.
Spacemacs, by default, allows you to jump through the code under the mouse pointer and highlights the pointed line. This is a no-no for me.
To disable the mouse, I resorted by adding under dotspacemacs/user-config the following line:

(xterm-mouse-mode -1)

To completely disable the mouse, this StackExchange answer hints at downloading the disable-mouse package and then using:

(global-disable-mouse-mode)

Make * and # search as in Vim

Apparently, I’m not the only one who noticed that * and # in Spacemacs do not do the RightThing(TM) out of the box: an Emacs StackExchange user asked this very question.
The answer is nice and effective: extend dotspacemacs/user-config with:

(with-eval-after-load 'auto-highlight-symbol
  (define-key evil-motion-state-map (kbd "*") 'evil-search-word-forward)
  (define-key evil-motion-state-map (kbd "#") 'evil-search-word-backward))

There’s still an unsolved nagging, though. Searches done through * and # do not populate the search history.

Make C-a and C-x increment and decrement numbers

For this one, I had to make a compromise. C-x is used as prefix for a gazillion other commands. I didn’t feel comfortable changing the prefix of all those commands: after all, incrementing and decrementing numbers is something I do regularly, yet not very often.

I decided I could settle for C-x a to increment and C-x x to decrement. Again, StackExchange comes to help: there a user suggests that the package “evil-numbers” does just that.

This time, dotspacemacs/user-config should be extended with these two lines:

(global-set-key (kbd "C-x a") 'evil-numbers/inc-at-pt)
(global-set-key (kbd "C-x x") 'evil-numbers/dec-at-pt)

Trim trailing spaces and newlines when saving a file

This is to comply with PEP8, mainly – though I admit I love not having trailing spaces at the end of the lines or unnecessary new lines at the end of a file.

Initially I found a solution that works fine, but is specific to plain emacs.

In Spacemacs, it turns out, there’s an easier baked-in way to do just this. Search under dotspacemacs/init the dotspace-whitespace-cleanup entry and set it to 'trailing.

Search history

If you search some term with / and then try again later by hitting first / and then the up arrow, you’ll be disappointed to see that Spacemacs comes out of the search mode.
Fear not, for there is a solution. Extend dotspacemacs/user-init with:

(setq-default evil-search-module 'evil-search)

Display the absolute path of a file (somewhere)

I’d love to see the full path of the file I am editing.
Since C-g is not an option, given that is a pretty fundamental command in Emacs, I’d be OK settling for SPC f y as the bug report suggests.

However, to always display the path of the file being edited, you can also extend your dotspacemacs/user-config function with:

(spaceline-define-segment buffer-id
  (if (buffer-file-name)
      (abbreviate-file-name (buffer-file-name))
      (powerline-buffer-id)))

Line numbers

This one is another “baked-in” value. In your dotspacemacs/init just set totspacemacs-line-numbers to something like t or prog-mode.

However, in the terminal, I saw that the line numbers are awfully close to the text. Others have noticed it and the fix is to add to your dotspacemacs/user-config the following lines:

(unless (display-graphic-p)
  (setq linum-format (concat linum-format " ")))

Lint as you write

In Vim I used to use python-mode.
Though sometimes it got confused and stopped working, it would catch most of the obvious issues, such as undefined names, unused variables, etc.

In Spacemacs there’s a similar checker, which is not enabled by default. For starters you have to uncomment syntax-checking under dotspacemacs-configuration-layers.
Once done, you can toggle the syntax checker using SPC t s.
To configure the checker (flake8, by default, if I’m not mistaken), create a ~/.config/flake8 file as described here.
You can list errors in a buffer by hitting SPC e L while there are a few other key combos worth learning.

I’ll update the post or write a follow-up on how to use pylint instead.

Misc

Opening files at a certain line

With Vim, I’d do vim file +8 or vim +8 file. Emacs only understand the latter: emacs +8 file.
The reason I mention this, is because I’ve set up a function that opens a file as specified by grep -rn.
To give an example, suppose that grep -rn ' x ' returns this:

somedir/fileA.txt:2873:  foo x bar
someotherdir/pyfile.py:28:from foo import x as y

I’d do v someotherdir/pyfile.py:28:from and be sure I’d open the file at that line.
So, the v() function would look like:

v() {
    FILE=$(echo "$1" | cut -d: -f1,2 --output-delimiter=" +")
    vim $FILE
}

Now, I have an e() function that differs only by the position of the arguments:

e() {
    FILE=$(echo "$1" | cut -d: -f1,2 --output-delimiter=" +" | awk '{print $2, $1}')
    emacs $FILE
}

Admittedly, it could all be done in awk, I believe, but spawning one more process is not critical in my situation.

Conclusions (for now)

There are still a few things I’m not very fond of, and I’ll try to address, namely:

  • There are no tabs in Spacemacs. I simply can’t live without tabs. It’ll be a hard compromise, this one, I’m afraid.
  • * and # to populate the search history – and persist across sessions! That one seems a bit fundamental to me.
  • Python motions given by python-mode. I noticed [M and ]M in Vim have been translated to [[ and ]] here. Not sure how to get to classes, yet. Probably minor anyway.
  • Learn how to create commands myself. Or re-learn a bunch of new commands. Again, not a biggie, I guess.
  • Start-up times. Spacemacs offers you lots, but it’s a behemoth when starting up. This can be sped up I believe, by running an emacs daemon.

Overall, the first impressions are good, but there’s still some road to do before I can say I’m alright in Spacemacs.

Till next time!

Publishing nosetests results in Jenkins CI

While trying to publish test results in Jenkins CI via the xUnit plugin, I’ve set up the Post-build Actions section in Jenkins according to what the nosetests documentation suggests, that is, to “tick the box named “Publish JUnit test result report”” and referenced the correct file.

However, I was repeatedly stumbling upon an error:

WARNING: The file '/var/lib/jenkins/workspace/Project/nosetests.xml' is an invalid file.
WARNING: At line 1 of file:/var/lib/jenkins/workspace/Project/nosetests.xml:cvc-complex-type.3.2.2: Attribute 'skip' is not allowed to appear in element 'testsuite'.
ERROR: The result file '/var/lib/jenkins/workspace/Project/nosetests.xml' for the metric 'JUnit' is not valid. The result file has been skipped.

Turns out, my nosetests.xml file does indeed contain the skip attribute. And that’s also in line with the example in the official documentation.
Much to my surprise, though, running a few web searches made me realize there aren’t many other users of Jenkins + xUnit plugin + nosetests who have the same problem.

To fix this, it looked like I had to write my one XLS file. All you need to do is create two templates, one matching the “skip” attribute, and the other matching everything else.

The “skip”-matching template will simply get rid of the attribute altogether. The other will pass everything else as it is.

By taking cue from this StackOverflow answer, the result is as follows:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" omit-xml-declaration="no" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="@skip" />
    <xsl:template match="@*|node()">
        
            <xsl:apply-templates select="@*|node()"/>
        
    


Save it as nosetests.xsl. Now, armed with your shiny XSL file, put it in the $JENKINS_HOME/userContent directory, say in userContent/xunit_xsl/nosetests.xsl.

In the job config page, under the Post-Build Actions section, change the type from JUnit to Custom, and provide a reference to the XSD file you’ve created, in the form of $JENKINS_HOME/userContent/xunit_xsl/nosetests.xsl.

Save, run, and happily enjoy your test results!

Rentalcars.com dark patterns

Wikipedia defines dark pattern “a user interface that has been carefully crafted to trick users into doing things”.

Then, “carefully crafting fake notifications about customers renting cars” clearly falls into the same category.

This is what rentalcars.com does. Once you select a city and some dates, you’ll get the list of cars available, and something else: a set of notifications that spawn with a random interval from one another implying other customers are renting cars in the same city you’re looking for a car to rent.

Does this put some pressure on you, hastening you into buying a car before all disappear? Yes.
Should you trust it? Actually, no.

If you inspect the source code, this is what you’ll see (click to view it full size):
rentalcarsdarkpattern
Have a look yourself and you’ll find that as the page loads, a function will set to display, in the top right corner, some “pre-cooked” notification, lasting for 4.8 seconds, popping at random intervals from one another.

Reload the same page a few times and you’ll see those 4 notifications pop up time and again.

Sure it can’t be that legal, can it?

Navigate the stack backward

Took me a bit to figure it out correctly, so here it is: a recursive generator to navigate the stack backward (upward, if you want)

def getUpperFrames(frame):
    yield frame
    if not hasattr(frame, 'f_back'):
        return
    for frame in getUpperFrames(frame.f_back):
        yield frame

And if you’re on Python 3 – which you should – then you get the bonus of using yield from instead of using for ... yield

Create a valid ISIN

For a suite of tests I recently wrote, I had to create valid ISINs. The official page doesn’t give away much on how the final checksum digit should be computed and the examples on Wikipedia are, in my opinion, not particularly clear.

While I was trying to understand more, I stumbled upon a related unanswered question on stack overflow, which was basically after what I was trying to do, so I took the time to answer it.

Here I report a slightly modified version of the code snippet linked above, to create the checksum digit for the first 11 characters of an ISIN.

import string

def digit_sum(n):
    return (n // 10) + (n % 10)

alphabet = {letter: value for (value, letter) in
            enumerate(''.join(str(n) for n in range(10)) + string.uppercase)}

def isinChecksumDigit(isin):
    isin_to_digits = ''.join(str(d) for d in (alphabet[v] for v in isin))
    isin_sum = 0
    for (i, c) in enumerate(reversed(isin_to_digits), 1):
        if i % 2 == 1:
            isin_sum += digit_sum(2*int(c))
        else:
            isin_sum += int(c)

    checksum_digit = abs(- isin_sum % 10)
    return checksum_digit

Assuming countries is a list of valid, i.e. ISO-6166 compliant, country codes, you call the isinChecksumDigit as follows:

In [1]: isin = '{:2s}{:09d}'.format(random.choice(countries), random.randint(1, 10E8))

In [2]: isin
Out[2]: 'KR681111517'

In [3]: validIsin = isin + str(isinChecksumDigit(isin))

In [4]: validIsin
Out[4]: 'KR6811115171