bookmark_borderAnnotations de type en python – Annoter ou convertir?

Lors d’une revue de code, un collègue, Zachary Paden, me demandait pourquoi j’appelais la fonction typing.cast sur mes variables plutôt que de créer des variables temporaires simplement pour typer. Eh bien, tout comme il ignorait l’existence de cast, j’ignorais que cette approche fonctionnait. En bon nerdz que nous sommes, il a décidé de mesurer la performance de chacune des approches.

Continue reading « Annotations de type en python – Annoter ou convertir? »

bookmark_borderMes recommendations de bières sans alcool, édition Septembre 2022

Depuis que j’ai fait le mois sans alcool en février dernier, j’ai pris conscience d’à quel point la consommation d’alcool a un impact sur la qualité de mon sommeil. En en prenant moins, je dors mieux et suis donc plus en forme.

Par chance, de nos jours, il y a une quantité surprenante de bières sans alcool disponibles. Je peux donc diminuer ma consommation d’alcool tout en continuant de boire de bonnes bières.

Alors voici mes recommendations, en images et peu de mots.

Continue reading « Mes recommendations de bières sans alcool, édition Septembre 2022 »

bookmark_borderMinimiser la taille de ses lambdas en supprimant boto3 du paquet à déployer

En avril dernier, j’ai écrit un billet sur la gestion de boto3 dans les paquets déployés. (Voir Minimiser la taille de ses lambdas en évitant d’avoir boto3 et ses stubs dans le paquet à déployer.) Cette approche fonctionne lorsque toutes les dépendances peuvent être dans la section développement.

Mais, qu’en est-il lorsque des dépendances sont requises en production? Par exemple, nous avons récemment eu à déployer un lambda qui dépend d’awswrangler. La technique suggérée dans le précédent billet ne fonctionnait donc pas. Ici, je vais couvrir une approche différente qui permet de supprimer boto3 du paquet à déployer dans le contexte cité en exemple. Elle est cependant moins flexible et se base sur une technologie spécifique: serverless.

Continue reading « Minimiser la taille de ses lambdas en supprimant boto3 du paquet à déployer »

bookmark_borderRecette: Exécuter une copie locale d’un site WordPress avec Docker

J’ai récemment décidé de publier mes articles en français et en anglais. Au préalable, je devais trouver et tester des plugins afin d’en choisir un. J’ai donc passé du temps, plus que je ne l’aurais cru, à le faire fonctionner localement à travers Docker. Comme j’ai rencontré quelques pièges, j’ai décidé de partager la recette.

Continue reading « Recette: Exécuter une copie locale d’un site WordPress avec Docker »

bookmark_borderMinimiser la taille de ses lambdas en évitant d’avoir boto3 et ses stubs dans le paquet à déployer

Si vous avez des problèmes de lambdas AWS qui excèdent la limite de 250Mo, voici une astuce qui pourrait vous aider.

Continue reading « Minimiser la taille de ses lambdas en évitant d’avoir boto3 et ses stubs dans le paquet à déployer »

bookmark_borderExplications et astuces d’utilisation de python poetry

Pour ceux qui sont habitués à utiliser un environnement virtuel «de base» avec virtualenv, poetry peut être déroutant. Voici quelques éclaircissements et certains trucs avec l’utilisation de poetry.

Continue reading « Explications et astuces d’utilisation de python poetry »

bookmark_borderJavaScript / Jest – Comment gérer les mocks

Pour faciliter les explications, voici une fonction triviale, qu’on peut supposer être une fonction importante à mocker.

exports.bob = () => {
  // fonction très TRÈS complexe...
  return "real";
}

Problème: Les mocks ne se réinitialisent pas automatiquement après chaque test. Il faut le faire manuellement et il y a deux méthodes équivalentes.

const index = require("./index")

test("test 1", () => {
  const original = index.bob;
  index.bob = jest.fn(() => 0);
  expect(index.bob(0)).toBe("mocked");
  index.bob = original; // <-- on remet la fonction originale en place
});

Ou encore

test("test 1", () => {
  const bob = jest.spyOn(index, "bob");
  bob.mockImplementation(() => "mocked");
  expect(index.bob(0)).toBe("mocked");
  addMock.mockRestore(); // <-- on remet la fonction originale en place
});

Oui, ça fonctionne, le hic c’est que si un test plante avant de réinitialiser le mock, les autres tests qui dépendent de ladite fonction planteront. Par exemple:

test("test 1", () => {
  original = index.bob;
  index.bob = jest.fn(() => 0);
  expect(index.bob(0)).toBe("mocked");
  throw "erreur"; // oups!
  index.bob = original; // ça n'arrive pas
});

test("test 2", () => {
  expect(index.bob(7)).toBe("real"); // va échouer car bob retourne encore 0
})

Solution 1 – try / finally

test("test 1", () => {
  original = index.bob;
  try {
    index.bob = jest.fn(() => "mocked");
    expect(index.bob(10)).toBe("mocked");
    throw "erreur"; // oups!
  } finally {
    index.bob = original;
  }
});

test("test 2", () => {
  expect(index.bob(7)).toBe("real");
})

C’est n’est pas tellement élégant car ça indente le code. C’est aussi facile d’oublier de mettre l’étape de réinitialisation dans un finally.

Solution 2 – Une fonction router / proxy

const original_bob = index.bob;
index.bob = jest.fn((arg) => {
  // un ou plusieurs if/else pour diriger les mocks
  if (arg === 0) {
    // mock pour "test 1"
    return "mocked";
  }

  // si rien ne correspond, on appelle la fonction originale
  return original_bob(arg);
});

test("test 1", () => {
  expect(index.bob(0)).toBe("mocked");
});

test("test 2", () => {
  expect(index.bob(10)).toBe("real");
})

Gros défaut: Ça ne fonctionne pas pour les fonctions qui n’ont pas d’argument. Autre inconvénient, la fonction peut rapidement devenir lourde s’il y a beaucoup de if / else. Pour ne pas perdre le fil, ça demande d’écrire des commentaires pour garder une trace de l’association entre chaque mock et test. Si on renomme un test sans mettre à jour le commentaire dans le mock, le lien est rompu.

De l’autre côté, cette approche a l’avantage de pouvoir réutiliser les mocks entre les tests sans devoir les reconfigurer à chaque test. De plus, elle n’exige pas au programmeur de devoir se rappeler à chaque fois d’encapsuler le mock dans un bloc try / finally et elle ne crée pas d’indentation du code.

Conclusion

Je théorise que la source du problème est probablement attribuable à l’absence de destructeurs en JavaScript, ce qui empêche d’encapsuler les mocks dans des objets et d’utiliser la technique resource acquisition is initialization, aussi connue sous RAII.

Merci JavaScript. Merci.

Références

bookmark_borderPièges de la fonction pour JavaScript Array Sort

Ah JavaScript, tu ne cesses de me surprendre par tes pièges. La fonction sort en a plus d’un. Commençons avec sa description.

La méthode sort() trie les éléments d’un tableau, dans ce même tableau, et renvoie le tableau. Par défaut, le tri s’effectue sur les éléments du tableau convertis en chaînes de caractères et triées selon les valeurs des unités de code UTF-16 des caractères. [Réf]

Emphase sur «convertis en chaînes de caractères». Il s’avère donc que le code suivant

data = [1, 2, 3, 20, 10]
data.sort();
console.log(data)

retourne

[ 1, 10, 2, 20, 3 ]

Il faut donc fournir sa propre fonction de tri. Mais, il y a encore un piège. Allons-y avec l’implémentation naïve.

data = [1,2,3,20,10]
data.sort((left, right) => left < right);
console.log(data)

retourne

[ 1, 2, 3, 20, 10 ]

Regardez où se situe le 20. Pas là où on s’attendrait. Le problème est que la fonction passée en argument à sort ne doit pas retourner un booléen, elle doit retourner un entier < 0, = 0 ou > 0. Donc, la bonne solution pour trier des entiers en ordre croissant est

data = [1, 2, 3, 20, 10]
data.sort((left, right) => left - right);
console.log(data)

ce qui retourne

[ 1, 2, 3, 10, 20 ]

Merci JavaScript. Merci.

Mise à jour 2022-02-20

Nicolas Kruchten m’a fait remarquer que Python, jusqu’à la version 2.4, avait le même design questionnable de la fonction passée à sort. Ils en gardent d’ailleurs la trace dans la documentation de la version courrante.