Writing, again and again

This commit is contained in:
Fred Pauchet 2022-05-11 15:43:09 +02:00
parent 2f4718c002
commit c15e05349d
11 changed files with 234 additions and 257 deletions

View File

@ -190,7 +190,7 @@ suivantes:
On fait confiance à django\_environ pour traduire la chaîne de On fait confiance à django\_environ pour traduire la chaîne de
connexion à la base de données. connexion à la base de données.
\end{itemize} \end{itemize}
\section{Création des répertoires de logs} \section{Création des répertoires de logs}
@ -198,16 +198,16 @@ suivantes:
mkdir -p /var/www/gwift/static mkdir -p /var/www/gwift/static
\end{verbatim} \end{verbatim}
\section{Socket} \section{Socket}
Dans le fichier \texttt{/etc/tmpfiles.d/gwift.conf}: Dans le fichier \texttt{/etc/tmpfiles.d/gwift.conf}:
\begin{verbatim} \begin{verbatim}
D /var/run/webapps 0775 gwift gunicorn_sockets - D /var/run/webapps 0775 gwift gunicorn_sockets -
\end{verbatim} \end{verbatim}
Suivi de la création par systemd : Suivi de la création par systemd :
\begin{verbatim} \begin{verbatim}
systemd-tmpfiles --create systemd-tmpfiles --create
\end{verbatim} \end{verbatim}
@ -316,13 +316,13 @@ l'aide de la commande \texttt{supervisorctl}:
\begin{verbatim} \begin{verbatim}
et 443 (HTTPS). et 443 (HTTPS).
\end{verbatim} \end{verbatim}
\begin{verbatim} \begin{verbatim}
firewall-cmd --permanent --zone=public --add-service=http firewall-cmd --permanent --zone=public --add-service=http
firewall-cmd --permanent --zone=public --add-service=https firewall-cmd --permanent --zone=public --add-service=https
firewall-cmd --reload firewall-cmd --reload
\end{verbatim} \end{verbatim}
\begin{itemize} \begin{itemize}
\item \item
On ouvre le port 80, uniquement pour autoriser une connexion HTTP, On ouvre le port 80, uniquement pour autoriser une connexion HTTP,
@ -338,14 +338,14 @@ l'aide de la commande \texttt{supervisorctl}:
yum install nginx -y yum install nginx -y
usermod -a -G gunicorn_sockets nginx usermod -a -G gunicorn_sockets nginx
\end{verbatim} \end{verbatim}
On configure ensuite le fichier \texttt{/etc/nginx/conf.d/gwift.conf}: On configure ensuite le fichier \texttt{/etc/nginx/conf.d/gwift.conf}:
\begin{verbatim} \begin{verbatim}
upstream gwift_app { upstream gwift_app {
server unix:/var/run/webapps/gunicorn_gwift.sock fail_timeout=0; server unix:/var/run/webapps/gunicorn_gwift.sock fail_timeout=0;
} }
server { server {
listen 80; listen 80;
server_name <server_name>; server_name <server_name>;
@ -362,7 +362,7 @@ l'aide de la commande \texttt{supervisorctl}:
gzip_types gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml; gzip_types gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml;
location /static/ { location /static/ {
access_log off; access_log off;
expires 30d; expires 30d;
add_header Pragma public; add_header Pragma public;
@ -372,7 +372,7 @@ l'aide de la commande \texttt{supervisorctl}:
} }
location / { location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_redirect off; proxy_redirect off;
@ -380,7 +380,7 @@ l'aide de la commande \texttt{supervisorctl}:
} }
} }
\end{verbatim} \end{verbatim}
\begin{itemize} \begin{itemize}
\item \item
Ce répertoire sera complété par la commande \texttt{collectstatic} que Ce répertoire sera complété par la commande \texttt{collectstatic} que
@ -450,3 +450,40 @@ Et dans le fichier crontab :
\section{Ansible} \section{Ansible}
\section{Docker-Compose}
(c/c Ced' - 2020-01-24)
Ça y est, j'ai fait un test sur mon portable avec docker et cookiecutter
pour django.
D'abords, après avoir installer docker-compose et les dépendances sous
debian, tu dois t'ajouter dans le groupe docker, sinon il faut être root
pour utiliser docker. Ensuite, j'ai relancé mon pc car juste relancé un
shell n'a pas suffit pour que je puisse utiliser docker avec mon compte.
Bon après c'est facile, un petit virtualenv pour cookiecutter, suivit
d'une installation du template django. Et puis j'ai suivi sans t
\url{https://cookiecutter-django.readthedocs.io/en/latest/developing-locally-docker.html}
Alors, il télécharge les images, fait un petit update, installe les
dépendances de dev, install les requirement pip \ldots\hspace{0pt}
Du coup, ça prend vite de la place: image.png
L'image de base python passe de 179 à 740 MB. Et là j'en ai pour presque
1,5 GB d'un coup.
Mais par contre, j'ai un python 3.7 direct et postgres 10 sans rien
faire ou presque.
La partie ci-dessous a été reprise telle quelle de
\href{https://cookiecutter-django.readthedocs.io/en/latest/deployment-with-docker.html}{la
documentation de cookie-cutter-django}.
le serveur de déploiement ne doit avoir qu'un accès en lecture au dépôt
source.
On peut aussi passer par fabric, ansible, chef ou puppet.

View File

@ -1,36 +0,0 @@
\chapter{Docker-Compose}
(c/c Ced' - 2020-01-24)
Ça y est, j'ai fait un test sur mon portable avec docker et cookiecutter
pour django.
D'abords, après avoir installer docker-compose et les dépendances sous
debian, tu dois t'ajouter dans le groupe docker, sinon il faut être root
pour utiliser docker. Ensuite, j'ai relancé mon pc car juste relancé un
shell n'a pas suffit pour que je puisse utiliser docker avec mon compte.
Bon après c'est facile, un petit virtualenv pour cookiecutter, suivit
d'une installation du template django. Et puis j'ai suivi sans t
\url{https://cookiecutter-django.readthedocs.io/en/latest/developing-locally-docker.html}
Alors, il télécharge les images, fait un petit update, installe les
dépendances de dev, install les requirement pip \ldots\hspace{0pt}
Du coup, ça prend vite de la place: image.png
L'image de base python passe de 179 à 740 MB. Et là j'en ai pour presque
1,5 GB d'un coup.
Mais par contre, j'ai un python 3.7 direct et postgres 10 sans rien
faire ou presque.
La partie ci-dessous a été reprise telle quelle de
\href{https://cookiecutter-django.readthedocs.io/en/latest/deployment-with-docker.html}{la
documentation de cookie-cutter-django}.
le serveur de déploiement ne doit avoir qu'un accès en lecture au dépôt
source.
On peut aussi passer par fabric, ansible, chef ou puppet.

View File

@ -1,2 +0,0 @@
\chapter{Graphs}

View File

@ -3,6 +3,8 @@
Deux types d'applications peuvent être considérées: les applications consommant intensivement des données (\textit{Data-intensive applications}) et les applications consommant principalement des cycles processeur (\textit{Compute-intensive applications}). Deux types d'applications peuvent être considérées: les applications consommant intensivement des données (\textit{Data-intensive applications}) et les applications consommant principalement des cycles processeur (\textit{Compute-intensive applications}).
Une application consommant principalement des données est rarement limitée par la puisse du CPU, mais plus souvent par la quantité et la complexité des structures de données, et la vitesse à laquelle celles-ci doivent être échangées. \cite[p. 3]{data_intensive} Une application consommant principalement des données est rarement limitée par la puisse du CPU, mais plus souvent par la quantité et la complexité des structures de données, et la vitesse à laquelle celles-ci doivent être échangées. \cite[p. 3]{data_intensive}
\section{Composants}
Nous pouvons distinguer plusieurs types de composants: Nous pouvons distinguer plusieurs types de composants:
\begin{enumerate} \begin{enumerate}
@ -11,7 +13,7 @@ Nous pouvons distinguer plusieurs types de composants:
\item \textbf{Les composants de supervision}, qui permettent de s'assurer que tout fonctionne correctement, et qu'aucun incident n'est prévu ou n'a été constaté récemment. \item \textbf{Les composants de supervision}, qui permettent de s'assurer que tout fonctionne correctement, et qu'aucun incident n'est prévu ou n'a été constaté récemment.
\end{enumerate} \end{enumerate}
\section{Composants fonctionnels} \subsection{Composants fonctionnels}
\begin{figure}[H] \begin{figure}[H]
\centering \centering
@ -35,18 +37,26 @@ Une application \textit{data-intensive} est généralement composée des blocs f
\hline \hline
\end{tabular} \end{tabular}
\subsection{Bases de données} \subsubsection{Bases de données}
\subsection{Tâches asynchrones} \subsubsection{Tâches asynchrones}
\subsection{Mise en cache} \subsubsection{Mise en cache}
\section{Composants périphériques} \subsection{Composants périphériques}
Les composants périphériques gravitent autour du fonctionnement normal de l'application. Les composants périphériques gravitent autour du fonctionnement normal de l'application.
Ce sont les "petites mains" qui aident à ce que l'application soit résiliente ou que la charge soit amoindrie ou répartie entre plusieurs instances. Ce sont les "petites mains" qui aident à ce que l'application soit résiliente ou que la charge soit amoindrie ou répartie entre plusieurs instances.
Il s'agit des composants comme le proxy inverse ou les distributeurs de charge: ce sont des composants qui sont fortement recommandés, mais pas obligatoires à au bon fonctionnement de l'application. Il s'agit des composants comme le proxy inverse ou les distributeurs de charge: ce sont des composants qui sont fortement recommandés, mais pas obligatoires à au bon fonctionnement de l'application.
\begin{quote}
Hard disks are reported having a mean time to failure (MTTF) of about 1° to 50 years.
Thus, on a storage cluster with 10,000 disks, we should expect on average one disk to die per day. \cite[p. 7]{data_design}
\end{quote}
Avoir des composants périphériques correctement configurés permet d'anticiper ce type d'erreurs, et donc d'augmenter la résilience de l'application. \index{Résilience}
Certaines plateformes, comme Amazon Web Services \index{AWS}, favorisent la flexibilité et l'eslasticité plutôt que la disponibilité d'une seule machine. \cite[p. 8]{data_design}
C'est donc une bonne idée de faire en sorte que des erreurs d'indisponibilité peuvent arriver \footnote{Netflix a par exemple développer le \href{https://github.com/netflix/chaosmonkey}{Chaos Monkey}, qui s'occupe d'éteindre au hasard des machines virtuels et containers dans un environnement de production, juste pour faire en sorte que les équipes soient drillées à développer toutes sortes de mécanismes de remise en service - }.
\begin{tabular}{|p{0.2\linewidth}|p{0.55\linewidth}|p{0.15\linewidth}|} \begin{tabular}{|p{0.2\linewidth}|p{0.55\linewidth}|p{0.15\linewidth}|}
\hline \hline
@ -62,6 +72,8 @@ Il s'agit des composants comme le proxy inverse ou les distributeurs de charge:
\hline \hline
Healthcheck & & Supervisord \\ Healthcheck & & Supervisord \\
\hline \hline
Télémétrie & & \\
\hline
\end{tabular} \end{tabular}
Si nous schématisons l'infrastructure et le chemin parcouru par une requête, nous pourrions arriver à la synthèse suivante: Si nous schématisons l'infrastructure et le chemin parcouru par une requête, nous pourrions arriver à la synthèse suivante:
@ -92,18 +104,26 @@ Si nous schématisons l'infrastructure et le chemin parcouru par une requête, n
\includegraphics{images/diagrams/architecture.png} \includegraphics{images/diagrams/architecture.png}
\subsection{Firewall} \subsubsection{Firewall}
\subsection{Reverse proxy} \subsubsection{Reverse proxy}
Le principe du \textbf{proxy inverse} est de pouvoir rediriger du trafic entrant vers une application hébergée sur le système. Il serait tout à fait possible de rendre notre application directement accessible depuis l'extérieur, mais le proxy a aussi l'intérêt de pouvoir élever la sécurité du serveur (SSL) et décharger le serveur applicatif grâce à un mécanisme de cache ou en compressant certains résultats \footnote{\url{https://fr.wikipedia.org/wiki/Proxy_inverse}} Le principe du \textbf{proxy inverse} est de pouvoir rediriger du trafic entrant vers une application hébergée sur le système. Il serait tout à fait possible de rendre notre application directement accessible depuis l'extérieur, mais le proxy a aussi l'intérêt de pouvoir élever la sécurité du serveur (SSL) et décharger le serveur applicatif grâce à un mécanisme de cache ou en compressant certains résultats \footnote{\url{https://fr.wikipedia.org/wiki/Proxy_inverse}}
\subsection{Répartiteur de charge (\textit{Load balancer})} \subsubsection{Répartiteur de charge (\textit{Load balancer})}
\subsection{Serveurs d'application (\textit{Workers})} \subsubsection{Serveurs d'application (\textit{Workers})}
\section{Composants de supervision} \subsubsection{Télémétrie}
\begin{quote}
Once a rocket has left the ground, telemetry is essential for tracking what's happening and for understanding failures \cite[p. 10]{data_design}
\end{quote}
Le
\subsection{Composants de supervision}
\begin{tabular}{|p{0.2\linewidth}|p{0.55\linewidth}|p{0.15\linewidth}|} \begin{tabular}{|p{0.2\linewidth}|p{0.55\linewidth}|p{0.15\linewidth}|}
\hline \hline
@ -117,9 +137,9 @@ Le principe du \textbf{proxy inverse} est de pouvoir rediriger du trafic entrant
\hline \hline
\end{tabular} \end{tabular}
\subsection{Supervision des processus} \subsubsection{Supervision des processus}
\subsection{Journaux} \subsubsection{Journaux}
La présence de journaux, leur structure et de la définition précise de leurs niveaux est essentielle; ce sont eux qui permettent d'obtenir des informations quant au statut de l'application: La présence de journaux, leur structure et de la définition précise de leurs niveaux est essentielle; ce sont eux qui permettent d'obtenir des informations quant au statut de l'application:
@ -185,7 +205,7 @@ Pour utiliser nos loggers, il suffit de copier le petit bout de code suivant:
\href{https://docs.djangoproject.com/en/stable/topics/logging/\#examples}{Par \href{https://docs.djangoproject.com/en/stable/topics/logging/\#examples}{Par
exemples}. exemples}.
\subsection{Exploitation des journaux} \subsubsection{Exploitation des journaux}
\begin{enumerate} \begin{enumerate}
\item \item

View File

@ -1,22 +1,32 @@
\chapter{Fiabilité, évolutivité et maintenabilité} \chapter{Fiabilité, évolutivité et maintenabilité}
\begin{quote} \begin{quote}
The primary cost of maintenance is in spelunking and risk The primary cost of maintenance is in spelunking and risk \cite[139]{clean_architecture}
\cite[139]{clean_architecture}
--- Robert C. Martin --- Robert C. Martin
\end{quote} \end{quote}
Pour la méthode de travail et de développement, nous allons nous baser Que l'utilisateur soit humain, bot automatique ou client Web, la finalité d'un développement est de fournir des applications résilientes, pouvant être mises à l'échelle et maintenables \cite[p. 6]{data_design} :
sur les \href{https://12factor.net/fr/}{The Twelve-factor App} - ou plus
simplement les \textbf{12 facteurs}. \begin{itemize}
\item
La résilience consiste à ce que l'application continue à fonctionner \textit{correctement} - c'est-à-dire à ce qu'elle fournisse un service correct au niveau de performance désiré, même quand les choses passent mal.
Cela signifie que ces systèmes ont la capacité d'anticiper certains types d'erreurs, ou en tout cas, de les gérer de manière propre.
\item
La mise à échelle consiste à autoriser le système à \textit{grandir} - soit par le trafic pouvant être pris en charge, soit par son volume de données, soit par sa complexité.
\item
Au fil du temps, il est probable que plusieurs personnes se succèdent à travailler sur l'évolution d'une application, qu'ils travaillent sur sa conception ou sur son exploitation.
La maintenabilité consiste à faire en sorte que toute intervention puisse être réalisée de manière productive.
\end{itemize}
Pour la méthode de travail et de développement, nous allons nous baser sur les \href{https://12factor.net/fr/}{The Twelve-factor App} - ou plus simplement les \textbf{12 facteurs}.
Suivre ces concepts permet de: Suivre ces concepts permet de:
\begin{enumerate} \begin{enumerate}
\item \item
\textbf{Faciliter la mise en place de phases d'automatisation}; plus \textbf{Faciliter la mise en place de phases d'automatisation}; plus
concrètement, de faciliter les mises à jour applicatives, simplifier concrètement, de faciliter les mises à jour applicatives, simplifier
la gestion de l'hôte qui héberge l'application ou les services, la gestion de l'hôte qui héberge l'application ou les services,
diminuer la divergence entre les différents environnements d'exécution et offrir la possibilité d'intégrer le diminuer la divergence entre les différents environnements d'exécution et offrir la possibilité d'intégrer le
projet dans un processus projet dans un processus
d'\href{https://en.wikipedia.org/wiki/Continuous_integration}{intégration d'\href{https://en.wikipedia.org/wiki/Continuous_integration}{intégration
@ -31,7 +41,7 @@ Suivre ces concepts permet de:
sur lesquels un projet pourrait être déployé, pour éviter de découvrir un bogue sur l'environnement de production qui serait impossible à reproduire ailleurs, simplement parce qu'un des composants varierait sur lesquels un projet pourrait être déployé, pour éviter de découvrir un bogue sur l'environnement de production qui serait impossible à reproduire ailleurs, simplement parce qu'un des composants varierait
\item \item
\textbf{Augmenter l'agilité générale du projet}, en permettant une \textbf{Augmenter l'agilité générale du projet}, en permettant une
meilleure évolutivité architecturale et une meilleure mise à l'échelle. meilleure évolutivité architecturale et une meilleure mise à l'échelle.
\end{enumerate} \end{enumerate}
En pratique, les points ci-dessus permettront de gagner un temps précieux à la construction d'un nouvel environnement - qu'il soit sur la machine du petit nouveau dans l'équipe, sur un serveur Azure/Heroku/Digital Ocean ou votre nouveau En pratique, les points ci-dessus permettront de gagner un temps précieux à la construction d'un nouvel environnement - qu'il soit sur la machine du petit nouveau dans l'équipe, sur un serveur Azure/Heroku/Digital Ocean ou votre nouveau
@ -43,18 +53,14 @@ cette méthode, nous avons:
\section{Une base de code unique, suivie par un contrôle de versions} \section{Une base de code unique, suivie par un contrôle de versions}
Chaque déploiement de l'application, et quel que soit l'environnement ciblé, se basera sur une source unique, afin de minimiser les différences que l'on pourrait trouver entre deux environnements d'un même projet. Chaque déploiement de l'application, et quel que soit l'environnement ciblé, se basera sur une source unique, afin de minimiser les différences que l'on pourrait trouver entre deux environnements d'un même projet.
Git est reconnu dans l'industrie comme standard des systèmes de contrôles de versions, malgré une courbe d'apprentissage assez ardue. Git est reconnu dans l'industrie comme standard des systèmes de contrôles de versions, malgré une courbe d'apprentissage assez ardue.
Comme dépôt, nous pourrons par exemple utiliser GitHub, Gitea ou Gitlab, suivant que vous ayez besoin d'une plateforme centralisée, propriétaire, payante ou auto-hébergée. \index{Git} \index{Github} \index{Gitlab} \index{Gitea} Comme dépôt, nous pourrons par exemple utiliser GitHub, Gitea ou Gitlab, suivant que vous ayez besoin d'une plateforme centralisée, propriétaire, payante ou auto-hébergée. \index{Git} \index{Github} \index{Gitlab} \index{Gitea}
\includegraphics{images/diagrams/12-factors-1.png} \includegraphics{images/diagrams/12-factors-1.png}
Comme l'explique Eran Messeri, ingénieur dans le groupe Google Developer Comme l'explique Eran Messeri, ingénieur dans le groupe Google Developer Infrastructure: "Un des avantages d'utiliser un dépôt unique de sources, est qu'il permet un accès facile et rapide à la forme la plus à jour du code, sans aucun besoin de coordination. \cite[pp. 288-298]{devops_handbook}.
Infrastructure: "Un des avantages d'utiliser un dépôt unique de sources, Ce cépôt n'est pas uniquement destiné à hébergé le code source, mais également à d'autres artefacts et autres formes de connaissance:
est qu'il permet un accès facile et rapide à la forme la plus à jour du
code, sans aucun besoin de coordination. \cite[pp. 288-298]{devops_handbook}.
Ce cépôt n'est pas uniquement destiné à hébergé le code source, mais
également à d'autres artefacts et autres formes de connaissance:
\begin{itemize} \begin{itemize}
\item \item
@ -73,30 +79,18 @@ Ce cépôt n'est pas uniquement destiné à hébergé le code source, mais
\section{Déclaration explicite et isolation des dépendances} \section{Déclaration explicite et isolation des dépendances}
Chaque installation ou configuration doit toujours être faite de la même Chaque installation ou configuration doit toujours être faite de la même manière, et doit pouvoir être répétée quel que soit l'environnement cible.
manière, et doit pouvoir être répétée quel que soit l'environnement Ceci permet d'éviter que l'application n'utilise une dépendance qui ne soit déjà installée sur un des sytèmes de développement, et qu'elle soit difficile, voire impossible, à répercuter sur un autre environnement.
cible. Dans le cas de Python, cela pourra être fait au travers de \href{https://pypi.org/project/pip/}{PIP - Package Installer for Python} ou \href{https://python-poetry.org/}{Poetry}.
Ceci permet d'éviter que l'application n'utilise une dépendance qui ne soit
déjà installée sur un des sytèmes de développement, et qu'elle soit
difficile, voire impossible, à répercuter sur un autre environnement.
Dans le cas de Python, cela pourra être fait au travers de
\href{https://pypi.org/project/pip/}{PIP - Package Installer for Python}
ou \href{https://python-poetry.org/}{Poetry}.
La majorité des langages moderners proposent des mécanismes similaires (\href{https://rubygems.org/}{Gem} pour Ruby, \href{https://www.npmjs.com/}{NPM} pour NodeJS, ...) La majorité des langages moderners proposent des mécanismes similaires (\href{https://rubygems.org/}{Gem} pour Ruby, \href{https://www.npmjs.com/}{NPM} pour NodeJS, ...)
Dans tous les cas, chaque application doit disposer d'un environnement sain, qui lui est assigné. Vu le peu de ressources que cela coûte, il ne faut pas s'en priver. Dans tous les cas, chaque application doit disposer d'un environnement sain, qui lui est assigné. Vu le peu de ressources que cela coûte, il ne faut pas s'en priver.
Chaque dépendance devra déclarer et épingler dans un fichier la version nécessaire. Chaque dépendance devra déclarer et épingler dans un fichier la version nécessaire.
Lors de la création d'un nouvel environnement vierge, il suffira d'utiliser ce Lors de la création d'un nouvel environnement vierge, il suffira d'utiliser ce fichier comme paramètre afin d'installer les prérequis au bon fonctionnement de notre application.
fichier comme paramètre afin d'installer les prérequis au bon
fonctionnement de notre application.
Ceci autorise une reproductibilité quasi parfaite de l'environnement. Ceci autorise une reproductibilité quasi parfaite de l'environnement.
Il est important de bien "épingler" les versions liées aux dépendances Il est important de bien "épingler" les versions liées aux dépendances de l'application. Cela peut éviter des effets de bord comme une nouvelle version d'une librairie dans laquelle un bug aurait pu avoir été introduit.
de l'application. Cela peut éviter des effets de bord comme une nouvelle Parce qu'il arrive que ce genre de problème apparaisse, et lorsque ce sera le cas, ce sera systématiquement au mauvais moment \footnote{Le paquet PyLint dépend par exemple d'Astroid; \href{https://github.com/PyCQA/pylint-django/issues/343}{en janvier 2022}, ce dernier a été mis à jour sans respecter le principe de versions sémantiques et introduisant une régression. PyLint spécifiait que sa dépendance avec Astroid devait respecter une version ~2.9. Lors de sa mise à jour en 2.9.1, Astroid a introduit un changement majeur, qui faisait planter Pylint. L'épinglage explicite aurait pu éviter ceci.}
version d'une librairie dans laquelle un bug aurait pu avoir été
introduit. Parce qu'il arrive que ce genre de problème apparaisse, et
lorsque ce sera le cas, ce sera systématiquement au mauvais moment \footnote{Le paquet PyLint dépend par exemple d'Astroid; \href{https://github.com/PyCQA/pylint-django/issues/343}{en janvier 2022}, ce dernier a été mis à jour sans respecter le principe de versions sémantiques et introduisant une régression. PyLint spécifiait que sa dépendance avec Astroid devait respecter une version ~2.9. Lors de sa mise à jour en 2.9.1, Astroid a introduit un changement majeur, qui faisait planter Pylint. L'épinglage explicite aurait pu éviter ceci.}
\section{Configuration applicative} \section{Configuration applicative}
@ -114,7 +108,7 @@ que:
... ...
\end{enumerate} \end{enumerate}
En pratique, toute information susceptible d'évoluer ou de changer (un seuil, une ressource externe, un couple utilisateur/mot de passe, ...) doit se trouver dans un fichier ou dans une variable d'environnement, et doit être facilement modifiable. En pratique, toute information susceptible d'évoluer ou de changer (un seuil, une ressource externe, un couple utilisateur/mot de passe, ...) doit se trouver dans un fichier ou dans une variable d'environnement, et doit être facilement modifiable.
Ceci permet de paramétrer facilement un environnement (par exemple, un container), simplement en modifiant une variable de configuration qui spécifierait la base de données sur laquelle l'application devra se connecter. Ceci permet de paramétrer facilement un environnement (par exemple, un container), simplement en modifiant une variable de configuration qui spécifierait la base de données sur laquelle l'application devra se connecter.
@ -128,32 +122,17 @@ Par exemple, Gitea expose \href{https://docs.gitea.io/en-us/config-cheat-sheet/}
\section{Ressources externes} \section{Ressources externes}
Nous parlons de bases de données, de services de mise en cache, d'API Nous parlons de bases de données, de services de mise en cache, d'API externes, ... L'application doit être capable d'effectuer des changements au niveau de ces ressources sans que son code ne soit modifié.
externes, ... L'application doit être capable d'effectuer Nous parlons alors de \textbf{ressources attachées}, dont la présence est nécessaire au bon fonctionnement de l'application, mais pour lesquelles le \textbf{type} n'est pas obligatoirement défini.
des changements au niveau de ces ressources sans que son code ne soit
modifié. Nous parlons alors de \textbf{ressources attachées}, dont la
présence est nécessaire au bon fonctionnement de l'application, mais
pour lesquelles le \textbf{type} n'est pas obligatoirement défini.
Nous voulons par exemple "une base de données" et "une mémoire cache", Nous voulons par exemple "une base de données" et "une mémoire cache", et pas "une base MariaDB et une instance Memcached".
et pas "une base MariaDB et une instance Memcached". De cette manière, De cette manière, les ressources peuvent être attachées et détachées d'un déploiement à la volée.
les ressources peuvent être attachées et détachées d'un déploiement à la
volée.
Si une base de données ne fonctionne pas correctement (problème matériel Si une base de données ne fonctionne pas correctement (problème matériel ?), l'administrateur pourrait simplement restaurer un nouveau serveur à partir d'une précédente sauvegarde, et l'attacher à l'application sans que le code source ne soit modifié. une solution consiste à passer toutes ces informations (nom du serveur et type de base de données, clé d'authentification, ... directement \emph{via} des variables d'environnement.
?), l'administrateur pourrait simplement restaurer un nouveau serveur à
partir d'une précédente sauvegarde, et l'attacher à l'application sans
que le code source ne soit modifié. une solution consiste à passer
toutes ces informations (nom du serveur et type de base de données, clé
d'authentification, ... directement \emph{via} des
variables d'environnement.
\includegraphics{images/12factors/attached-resources.png} \includegraphics{images/12factors/attached-resources.png}
Nous serons ravis de pouvoir simplement modifier une chaîne Nous serons ravis de pouvoir simplement modifier une chaîne \texttt{sqlite:////tmp/my-tmp-sqlite.db} en \texttt{psql://user:pass@127.0.0.1:8458/db} lorsque ce sera nécessaire, sans avoir à recompiler ou redéployer les modifications.
\texttt{sqlite:////tmp/my-tmp-sqlite.db} en
\texttt{psql://user:pass@127.0.0.1:8458/db} lorsque ce sera nécessaire,
sans avoir à recompiler ou redéployer les modifications.
Ces ressources sont donc spécifiés grâce à des variables d'environnement, et chacune d'entre elles dispose également d'un \textbf{type}, afin de profiter d'une correspondance dynamique entre un moteur d'exécution et une information de configuration. Ces ressources sont donc spécifiés grâce à des variables d'environnement, et chacune d'entre elles dispose également d'un \textbf{type}, afin de profiter d'une correspondance dynamique entre un moteur d'exécution et une information de configuration.
\section{Séparation des phases de construction} \section{Séparation des phases de construction}
@ -181,8 +160,8 @@ sur les \emph{releases} de Gitea, sur un serveur d'artefacts (\href{https://fr.w
Toute information stockée en mémoire ou sur disque ne doit pas altérer le comportement futur de l'application, par exemple après un redémarrage non souhaité. Toute information stockée en mémoire ou sur disque ne doit pas altérer le comportement futur de l'application, par exemple après un redémarrage non souhaité.
Dit autrement, l'exécution de l'application ne doit pas dépendre de la présence d'une information stockée en mémoire ou sur disque. Dit autrement, l'exécution de l'application ne doit pas dépendre de la présence d'une information stockée en mémoire ou sur disque.
Pratiquement, si l'application devait rencontrer un problème, il est nécessaire qu'elle puisse redémarrer rapidement, éventuellement en étant déployée sur un autre serveur - par exemple suite à un problème matériel. Pratiquement, si l'application devait rencontrer un problème, il est nécessaire qu'elle puisse redémarrer rapidement, éventuellement en étant déployée sur un autre serveur - par exemple suite à un problème matériel.
Toute information stockée physiquement sur le premier hôte durant son exécution sur le premier hôte, puisqu'elle pourra avoir été entretemps perdue. Toute information stockée physiquement sur le premier hôte durant son exécution sur le premier hôte, puisqu'elle pourra avoir été entretemps perdue.
Lors d'une initialisation ou réinitialisation, la solution consiste donc à jouer sur les variables d'environnement (cf. \#3) et sur les informations que l'on pourra trouver au niveau des ressources attachées (cf \#4), et faire en sorte que les informations et données primordiales puissent être récupérées ou reconstruites. Lors d'une initialisation ou réinitialisation, la solution consiste donc à jouer sur les variables d'environnement (cf. \#3) et sur les informations que l'on pourra trouver au niveau des ressources attachées (cf \#4), et faire en sorte que les informations et données primordiales puissent être récupérées ou reconstruites.
Il serait également difficile d'appliquer une mise à l'échelle de l'application, en ajoutant un nouveau serveur d'exécution, si une donnée indispensable à son fonctionnement devait se trouver sur la seule machine où elle est actuellement exécutée. Il serait également difficile d'appliquer une mise à l'échelle de l'application, en ajoutant un nouveau serveur d'exécution, si une donnée indispensable à son fonctionnement devait se trouver sur la seule machine où elle est actuellement exécutée.
@ -200,9 +179,9 @@ en HTTP ou via un autre protocole.
L'applicatoin fonctionne de manière autonome et expose un port (ici, le 8000). L'applicatoin fonctionne de manière autonome et expose un port (ici, le 8000).
Le serveur (= l'hôte) choisit d'appliquer une correspondance entre "son" port 443 et le port offert par l'application (8000). Le serveur (= l'hôte) choisit d'appliquer une correspondance entre "son" port 443 et le port offert par l'application (8000).
\section{Connaissance et confiance des processys systèmes} \section{Connaissance et confiance des processus systèmes}
Comme décrit plus haut (cf. \#6), l'application doit utiliser des processus \emph{stateless} (sans état). Nous pouvons créer et utiliser des processus supplémentaires pour tenir plus facilement une lourde charge, ou dédier des particuliers pour certaines tâches: requêtes HTTP \emph{via} des processus Web; \emph{long-running} jobs pour des processus asynchrones, ... Comme décrit plus haut (cf. \#6), l'application doit utiliser des processus \emph{stateless} (sans état). Nous pouvons créer et utiliser des processus supplémentaires pour tenir plus facilement une lourde charge, ou dédier des particuliers pour certaines tâches: requêtes HTTP \emph{via} des processus Web; \emph{long-running} jobs pour des processus asynchrones, ...
Si cela existe sur l'hôte hébergeant l'application, ne vous fatiguez pas: utilisez le. Si cela existe sur l'hôte hébergeant l'application, ne vous fatiguez pas: utilisez le.
\includegraphics{images/12factors/process-types.png} \includegraphics{images/12factors/process-types.png}
@ -211,12 +190,12 @@ Si cela existe sur l'hôte hébergeant l'application, ne vous fatiguez pas: util
Par "arrêt élégant", nous voulons surtout éviter le fameux Par "arrêt élégant", nous voulons surtout éviter le fameux
\texttt{kill\ -9\ \textless{}pid\textgreater{}} (ou équivalent), ou tout autre arrêt brutal d'un processus qui nécessiterait une intervention urgente du \texttt{kill\ -9\ \textless{}pid\textgreater{}} (ou équivalent), ou tout autre arrêt brutal d'un processus qui nécessiterait une intervention urgente du
superviseur. superviseur.
En prévoyant une manière élégante d'envoyer un signal de terminaison, En prévoyant une manière élégante d'envoyer un signal de terminaison,
\begin{enumerate} \begin{enumerate}
\item Les requêtes en cours peuvent se terminer au mieux, \item Les requêtes en cours peuvent se terminer au mieux,
\item Le démarrage rapide de nouveaux processus améliorera la balance d'un processus en cours d'extinction vers des processus tout frais, en autorisant l'exécution parallèle d'anciens et de nouveaux "types" de processus \item Le démarrage rapide de nouveaux processus améliorera la balance d'un processus en cours d'extinction vers des processus tout frais, en autorisant l'exécution parallèle d'anciens et de nouveaux "types" de processus
\end{enumerate} \end{enumerate}
L'intégration de ces mécanismes dès les premières étapes de L'intégration de ces mécanismes dès les premières étapes de
@ -228,60 +207,51 @@ hôte, etc.).
\section{Similarité des environnements} \section{Similarité des environnements}
Conserver les différents environnements aussi similaires Conserver les différents environnements aussi similaires que possible, et limiter les divergences entre un environnement de développement et de production.
que possible, et limiter les divergences entre un environnement de
développement et de production
L'exemple donné est un développeur qui utilise macOS, NGinx et SQLite, tandis que l'environnement de production tourne sur une CentOS avec Apache2 et PostgreSQL. L'exemple donné est un développeur qui utilise macOS, NGinx et SQLite, tandis que l'environnement de production tourne sur une CentOS avec Apache2 et PostgreSQL.
Faire en sorte que tous les environnements soient les plus similaires possibles limite les divergences entre environnements, facilite les déploiements et limite la casse et la découverte de modules non compatibles, au plus proche de la phase de développement, selon le principe de la corde d'Andon \cite[p. 140]{devops_handbook} \index{Andon} \footnote{Pour donner un exemple tout bête, SQLite utilise un Faire en sorte que tous les environnements soient les plus similaires possibles limite les divergences entre environnements, facilite les déploiements et limite la casse et la découverte de modules non compatibles, au plus proche de la phase de développement, selon le principe de la corde d'Andon \cite[p. 140]{devops_handbook} \index{Andon} \footnote{Pour donner un exemple tout bête, SQLite utilise un
\href{https://www.sqlite.org/datatype3.html}{mécanisme de stockage dynamique}, associée à la valeur plutôt qu'au schéma, \emph{via} un système d'affinités. Un autre moteur de base de données définira un schéma statique et rigide, où la valeur sera déterminée par son contenant. \href{https://www.sqlite.org/datatype3.html}{mécanisme de stockage dynamique}, associée à la valeur plutôt qu'au schéma, \emph{via} un système d'affinités. Un autre moteur de base de données définira un schéma statique et rigide, où la valeur sera déterminée par son contenant.
Un champ \texttt{URLField} proposé par Django a une longeur maximale par défaut de \href{https://docs.djangoproject.com/en/3.1/ref/forms/fields/\#django.forms.URLField}{200 caractères}. Un champ \texttt{URLField} proposé par Django a une longeur maximale par défaut de \href{https://docs.djangoproject.com/en/3.1/ref/forms/fields/\#django.forms.URLField}{200 caractères}.
Si vous faites vos développements sous SQLite et que vous rencontrez une URL de plus de 200 caractères, votre développement sera passera parfaitement bien, mais plantera en production (ou en \emph{staging}, si vous faites les choses peu mieux) parce que les données seront tronquées, et que cela ne plaira pas à la base de données. Si vous faites vos développements sous SQLite et que vous rencontrez une URL de plus de 200 caractères, votre développement sera passera parfaitement bien, mais plantera en production (ou en \emph{staging}, si vous faites les choses peu mieux) parce que les données seront tronquées, et que cela ne plaira pas à la base de données.
Conserver des environements similaires limite ce genre de désagréments.} Conserver des environements similaires limite ce genre de désagréments.}
Ceci permet également de proposer à nos utilisateurs un bac à sable dans lequel ils pourront explorer et réaliser des expérimentations en toute sécurité, sans quel cela n'ait d'impact sur un \textit{réel} environnement de production, où les conséquences pourraient être beaucoup plus graves. \cite[p. 9]{data_design}
\section{Journaux de flux évènementiels} \section{Journaux de flux évènementiels}
Une application ne doit jamais se soucier de l'endroit où les évènements qui la concerne seront écrits, mais se doit simplement de les envoyer sur la sortie \texttt{stdout}. Une application ne doit jamais se soucier de l'endroit où les évènements qui la concerne seront écrits, mais se doit simplement de les envoyer sur la sortie \texttt{stdout}.
De cette manière, que nous soyons en développement sur le poste d'un développeur avec une sortie console ou sur une machine de production avec un envoi vers une instance \href{https://www.graylog.org/}{Greylog} ou \href{https://sentry.io/welcome/}{Sentry}, le routage des journaux sera réalisé en fonction de sa nécessité et de sa criticité, et non pas parce que le développeur l'a spécifié en dur dans son code. De cette manière, que nous soyons en développement sur le poste d'un développeur avec une sortie console ou sur une machine de production avec un envoi vers une instance \href{https://www.graylog.org/}{Greylog} ou \href{https://sentry.io/welcome/}{Sentry}, le routage des journaux sera réalisé en fonction de sa nécessité et de sa criticité, et non pas parce que le développeur l'a spécifié en dur dans son code.
Cette phase est critique, dans la mesure où les journaux d'exécution sont la seule manière pour une application de communiquer son état vers l'extérieur: recevoir une erreur interne de serveur est une chose; pouvoir obtenir un minimum d'informations, voire un contexte de plantage complet en est une autre. Cette phase est critique, dans la mesure où les journaux d'exécution sont la seule manière pour une application de communiquer son état vers l'extérieur: recevoir une erreur interne de serveur est une chose; pouvoir obtenir un minimum d'informations, voire un contexte de plantage complet en est une autre.
La différence entre ces deux points vous fera, au mieux, gagner plusieurs heures sur l'identification ou la résolution d'un problème. La différence entre ces deux points vous fera, au mieux, gagner plusieurs heures sur l'identification ou la résolution d'un problème.
\section{Isolation des tâches administratives} \section{Isolation des tâches administratives}
Evitez qu'une migration ne puisse être démarrée depuis une URL de l'application, ou qu'un envoi massif de notifications ne soit accessible pour n'importe quel utilisateur: les tâches administratives ne doivent être accessibles qu'à un administrateur. Evitez qu'une migration ne puisse être démarrée depuis une URL de l'application, ou qu'un envoi massif de notifications ne soit accessible pour n'importe quel utilisateur: les tâches administratives ne doivent être accessibles qu'à un administrateur.
Les applications 12facteurs favorisent les langages qui mettent un environnement REPL (pour \emph{Read}, \emph{Eval}, \emph{Print} et \emph{Loop}) \index{REPL} à disposition (au hasard: \href{https://pythonprogramminglanguage.com/repl/}{Python} ou \href{https://kotlinlang.org/}{Kotlin}), ce qui facilite les étapes de maintenance. Les applications 12facteurs favorisent les langages qui mettent un environnement REPL (pour \emph{Read}, \emph{Eval}, \emph{Print} et \emph{Loop}) \index{REPL} à disposition (au hasard: \href{https://pythonprogramminglanguage.com/repl/}{Python} ou \href{https://kotlinlang.org/}{Kotlin}), ce qui facilite les étapes de maintenance.
\section{Conclusions} \section{Conclusions}
Une application devient nettement plus maintenable dès lors que l'équipe Une application devient nettement plus maintenable dès lors que l'équipe de développement suit de près les différentes étapes de sa conception, de la demande jusqu'à son aboutissement en production. \cite[pp. 293-294]{devops_handbook}.
de développement suit de près les différentes étapes de sa conception,
de la demande jusqu'à son aboutissement en production.
\cite[pp. 293-294]{devops_handbook}.
Au fur et à mesure que le code est délibérément construit pour être maintenable, l'équipe gagne en rapidité, en qualité et en fiabilité de déploiement, ce qui facilite les tâches opérationnelles: Au fur et à mesure que le code est délibérément construit pour être maintenable, l'équipe gagne en rapidité, en qualité et en fiabilité de déploiement, ce qui facilite les tâches opérationnelles:
\begin{enumerate} \begin{enumerate}
\item \item
Activation d'une télémétrie suffisante dans les applications et les Activation d'une télémétrie suffisante dans les applications et les environnements
environnements
\item \item
Conservation précise des dépendances nécessaires Conservation précise des dépendances nécessaires
\item \item
Résilience des services et plantage élégant (i.e. \textbf{sans finir Résilience des services et plantage élégant (i.e. \textbf{sans finir un SEGFAULT avec l'OS dans les choux et un écran bleu})
sur un SEGFAULT avec l'OS dans les choux et un écran bleu})
\item \item
Compatibilité entre les différentes versions (n+1, \ldots\hspace{0pt}) Compatibilité entre les différentes versions (n+1, \ldots)
\item \item
Gestion de l'espace de stockage associé à un environnement (pour Gestion de l'espace de stockage associé à un environnement (pour éviter d'avoir un environnement de production qui fait 157 Tera-octets)
éviter d'avoir un environnement de production qui fait 157
Tera-octets)
\item \item
Activation de la recherche dans les logs Activation de la recherche dans les logs
\item \item
Traces des requêtes provenant des utilisateurs, indépendamment des Traces des requêtes provenant des utilisateurs, indépendamment des services utilisés
services utilisés
\item \item
Centralisation de la configuration (\textbf{via} ZooKeeper, par Centralisation de la configuration (\textbf{via} ZooKeeper, par exemple)
exemple)
\end{enumerate} \end{enumerate}

View File

@ -1,8 +0,0 @@
\chapter{Poésie de la programmation}
\begin{quote}
The primary cost of maintenance is in spelunking and risk
\cite[139]{clean_architecture}
--- Robert C. Martin
\end{quote}

View File

@ -8,7 +8,7 @@ Le langage \href{https://www.python.org/}{Python} est un \href{https://docs.pyth
\caption{\url{https://xkcd.com/353/}} \caption{\url{https://xkcd.com/353/}}
\end{figure} \end{figure}
A première vue, et suivants les autres langages que vous connaitriez ou auriez déjà abordé, certains concepts restent difficiles à aborder: l'indentation définit l'étendue d'un bloc (classe, fonction, méthode, boucle, condition, il n'y a pas de typage fort des variables et le compilateur n'est pas là pour assurer le filet de sécurité avant la mise en production (puisqu'il n'y a pas de compilateur). A première vue, et suivants les autres langages que vous connaitriez ou auriez déjà abordé, certains concepts restent difficiles à aborder: l'indentation définit l'étendue d'un bloc (classe, fonction, méthode, boucle, condition, il n'y a pas de typage fort des variables et le compilateur n'est pas là pour assurer le filet de sécurité avant la mise en production (puisqu'il n'y a pas de compilateur).
Et malgré ces quelques points, Python reste un langage généraliste accessible et "bon partout", et de pouvoir se reposer sur un écosystème stable et fonctionnel. Et malgré ces quelques points, Python reste un langage généraliste accessible et "bon partout", et de pouvoir se reposer sur un écosystème stable et fonctionnel.
Il fonctionne avec un système d'améliorations basées sur des propositions: les PEP, ou "\textbf{Python Enhancement Proposal}\index{PEP}". Il fonctionne avec un système d'améliorations basées sur des propositions: les PEP, ou "\textbf{Python Enhancement Proposal}\index{PEP}".
@ -19,7 +19,7 @@ Le langage Python utilise un typage dynamique appelé \href{https://fr.wikipedia
\begin{quote} \begin{quote}
"\emph{When I see a bird that quacks like a duck, walks like a duck, has "\emph{When I see a bird that quacks like a duck, walks like a duck, has
feathers and webbed feet and associates with ducks --- I'm certainly feathers and webbed feet and associates with ducks --- I'm certainly
going to assume that he is a duck}" going to assume that he is a duck}"
-- Source: \href{http://en.wikipedia.org/wiki/Duck_test}{Wikipedia}. -- Source: \href{http://en.wikipedia.org/wiki/Duck_test}{Wikipedia}.
\end{quote} \end{quote}
@ -34,7 +34,7 @@ ressources pourraient vous aider:
with Python} \cite{boring_stuff}, aka. \emph{Practical with Python} \cite{boring_stuff}, aka. \emph{Practical
Programming for Total Beginners} Programming for Total Beginners}
\item \item
\textbf{Pour un (gros) niveau au dessus} et pour un état de l'art du langage, nous ne pouvons que vous recommander le livre Expert Python Programming \cite{expert_python}, qui aborde énormément d'aspects du langage en détails (mais pas toujours en profondeur): les différents types d'interpréteurs, les éléments de langage avancés, différents outils de productivité, métaprogrammation, optimisation de code, programmation orientée évènements, multithreading et concurrence, tests, ... \textbf{Pour un (gros) niveau au dessus} et pour un état de l'art du langage, nous ne pouvons que vous recommander le livre Expert Python Programming \cite{expert_python}, qui aborde énormément d'aspects du langage en détails (mais pas toujours en profondeur): les différents types d'interpréteurs, les éléments de langage avancés, différents outils de productivité, métaprogrammation, optimisation de code, programmation orientée évènements, multithreading et concurrence, tests, ...
A ce jour, c'est le concentré de sujets liés au langage le plus intéressant qui ait pu arriver entre nos mains. A ce jour, c'est le concentré de sujets liés au langage le plus intéressant qui ait pu arriver entre nos mains.
\end{itemize} \end{itemize}
@ -60,21 +60,21 @@ permet de surcharger l'initialisation d'une instance de classe.
\end{minted} \end{minted}
\end{listing} \end{listing}
Ces méthodes, utilisées seules ou selon des combinaisons spécifiques, constituent les \emph{protocoles de langage}. Ces méthodes, utilisées seules ou selon des combinaisons spécifiques, constituent les \emph{protocoles de langage}.
Une liste complètement des \emph{dunder methods} peut être trouvée dans la section \texttt{Data\ Model} de \href{https://docs.python.org/3/reference/datamodel.html}{la documentation du langage Python}. Une liste complètement des \emph{dunder methods} peut être trouvée dans la section \texttt{Data\ Model} de \href{https://docs.python.org/3/reference/datamodel.html}{la documentation du langage Python}.
Tous les opérateurs sont également exposés comme des fonctions ordinaires du module \texttt{opeartor}, dont la \href{https://docs.python.org/3.9/library/operator.html}{documentation} donne un bon aperçu. Tous les opérateurs sont également exposés comme des fonctions ordinaires du module \texttt{opeartor}, dont la \href{https://docs.python.org/3.9/library/operator.html}{documentation} donne un bon aperçu.
Indiquer qu'un type d'objet implémente un protocole du langage indique simplement qu'il est compatible avec une partie spécifique de la syntaxe du langage Python. Indiquer qu'un type d'objet implémente un protocole du langage indique simplement qu'il est compatible avec une partie spécifique de la syntaxe du langage Python.
Vous trouverez ci-dessous un tableau reprenant les protocoles les plus courants: Vous trouverez ci-dessous un tableau reprenant les protocoles les plus courants:
\begin{center} \begin{center}
\begin{tabular}{ c c c } \begin{tabular}{ c c c }
cell1 & cell2 & cell3 \\ cell1 & cell2 & cell3 \\
cell4 & cell5 & cell6 \\ cell4 & cell5 & cell6 \\
cell7 & cell8 & cell9 cell7 & cell8 & cell9
\end{tabular} \end{tabular}
\end{center} \end{center}
Les points principaux à présenter ci-dessus: Les points principaux à présenter ci-dessus:
@ -103,15 +103,15 @@ L'exemple ci-dessous implémente la soustraction de deux matrices:
\begin{listing} \begin{listing}
\begin{minted}[tabsize=4]{python} \begin{minted}[tabsize=4]{python}
def __sub__(self, other): def __sub__(self, other):
if (len(self.rows) != len(other.rows) if (len(self.rows) != len(other.rows)
or len(self.rows[0]) != len(other.rows[0]) or len(self.rows[0]) != len(other.rows[0])
): ):
raise ValueError("Matrix dimensions don't match") raise ValueError("Matrix dimensions don't match")
return Matrix( return Matrix(
[ [
[a - b for a, b in zip(a_row, b_row)] [a - b for a, b in zip(a_row, b_row)]
for a_row, b_row in zip(self.rows, other.rows) for a_row, b_row in zip(self.rows, other.rows)
] ]
) )
\end{minted} \end{minted}
@ -119,7 +119,7 @@ L'exemple ci-dessous implémente la soustraction de deux matrices:
Il suffit dès lors de réaliser la soustraction matricielle entre deux objets de type \texttt{Matrix} pour que la méthode \texttt{sub())} ci-dessous soit appelée. Il suffit dès lors de réaliser la soustraction matricielle entre deux objets de type \texttt{Matrix} pour que la méthode \texttt{sub())} ci-dessous soit appelée.
En fait, l'intérêt concerne surtout la représentation de nos modèles, puisque chaque classe du modèle est représentée par la définition d'un objet Python. En fait, l'intérêt concerne surtout la représentation de nos modèles, puisque chaque classe du modèle est représentée par la définition d'un objet Python.
Nous pouvons donc utiliser ces mêmes \textbf{dunder methods} (\textbf{double-underscores methods}) pour étoffer les protocoles du langage. Nous pouvons donc utiliser ces mêmes \textbf{dunder methods} (\textbf{double-underscores methods}) pour étoffer les protocoles du langage.
\section{The Zen of Python} \section{The Zen of Python}
@ -148,19 +148,19 @@ Nous pouvons donc utiliser ces mêmes \textbf{dunder methods} (\textbf{double-un
If the implementation is hard to explain, it's a bad idea. If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea. If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those! Namespaces are one honking great idea -- let's do more of those!
\end{verbatim} \end{verbatim}
\caption{The Zen of Python} \caption{The Zen of Python}
\end{listing} \end{listing}
\section{Guide de style} \section{Guide de style}
La première PEP qui va nous intéresser est la PEP8. La première PEP qui va nous intéresser est la PEP8.
Elle spécifie comment du code Python doit être organisé ou formaté, quelles sont les conventions pour l'indentation, le nommage des variables et des classes, ... Elle spécifie comment du code Python doit être organisé ou formaté, quelles sont les conventions pour l'indentation, le nommage des variables et des classes, ...
En bref, elle décrit comment écrire du code proprement, afin que d'autres développeurs puissent le reprendre facilement, ou simplement que votre base de code ne dérive lentement vers un seuil de non-maintenabilité. En bref, elle décrit comment écrire du code proprement, afin que d'autres développeurs puissent le reprendre facilement, ou simplement que votre base de code ne dérive lentement vers un seuil de non-maintenabilité.
Dans cet objectif, un outil existe et listera l'ensemble des conventions qui ne sont pas correctement suivies dans votre projet: flake8. Pour l'installer, passez par pip. Dans cet objectif, un outil existe et listera l'ensemble des conventions qui ne sont pas correctement suivies dans votre projet: flake8. Pour l'installer, passez par pip.
Lancez ensuite la commande \texttt{flake8} suivie du chemin à analyser (\texttt{.}, le nom d'un répertoire, le nom d'un fichier \texttt{.py}, ...). Lancez ensuite la commande \texttt{flake8} suivie du chemin à analyser (\texttt{.}, le nom d'un répertoire, le nom d'un fichier \texttt{.py}, ...).
Si vous souhaitez uniquement avoir le nombre d'erreur de chaque type, saisissez les options \texttt{-\/-statistics\ -qq} - l'attribut \texttt{-qq} permettant simplement d'ignorer toute sortie console autre que les statistiques demandées). Si vous souhaitez uniquement avoir le nombre d'erreur de chaque type, saisissez les options \texttt{-\/-statistics\ -qq} - l'attribut \texttt{-qq} permettant simplement d'ignorer toute sortie console autre que les statistiques demandées).
\begin{listing}[!ht] \begin{listing}[!ht]
@ -182,26 +182,26 @@ Si vous ne voulez pas être dérangé sur votre manière de coder, et que vous v
\begin{listing}[!ht] \begin{listing}[!ht]
\begin{verbatim} \begin{verbatim}
$ pyflakes . $ pyflakes .
... ...
\end{verbatim} \end{verbatim}
\caption{Une utilisation de pyflakes} \caption{Une utilisation de pyflakes}
\end{listing} \end{listing}
A noter qu'un greffon pour \texttt{flake8} existe et donnera une estimation de la complexité de McCabe pour les fonctions trop complexes. A noter qu'un greffon pour \texttt{flake8} existe et donnera une estimation de la complexité de McCabe pour les fonctions trop complexes.
Installez-le avec \texttt{pip\ install\ mccabe}, et activez-le avec le paramètre \texttt{-\/-max-complexity}. Installez-le avec \texttt{pip\ install\ mccabe}, et activez-le avec le paramètre \texttt{-\/-max-complexity}.
Toute fonction dans la complexité est supérieure à cette valeur sera considérée comme trop complexe. Toute fonction dans la complexité est supérieure à cette valeur sera considérée comme trop complexe.
\section{Conventions de documentation} \section{Conventions de documentation}
Python étant un langage interprété fortement typé, il est plus que conseillé, au même titre que les tests unitaires que nous verrons plus bas, de documenter son code. Python étant un langage interprété fortement typé, il est plus que conseillé, au même titre que les tests unitaires que nous verrons plus bas, de documenter son code.
Ceci impose une certaine rigueur, tout en améliorant énormément la qualité, la compréhension et la reprise du code par une tierce personne. Ceci impose une certaine rigueur, tout en améliorant énormément la qualité, la compréhension et la reprise du code par une tierce personne.
Ceci implique aussi de \textbf{tout} documenter: les modules, les paquets, les classes, les fonctions, méthodes, ... ce qui peut aller à contrecourant d'autres pratiques \cite{clean_code}{53-74} ; il y a donc une juste mesure à prendre entre "tout documenter" et "tout bien documenter": Ceci implique aussi de \textbf{tout} documenter: les modules, les paquets, les classes, les fonctions, méthodes, ... ce qui peut aller à contrecourant d'autres pratiques \cite{clean_code}{53-74} ; il y a donc une juste mesure à prendre entre "tout documenter" et "tout bien documenter":
\begin{itemize} \begin{itemize}
\item \item
Inutile d'ajouter des watermarks, auteurs, ... Inutile d'ajouter des watermarks, auteurs, ...
Git ou tout VCS s'en sortira très bien et sera beaucoup plus efficace que n'importe quelle chaîne de caractères que vous pourriez indiquer et qui sera fausse dans six mois, Git ou tout VCS s'en sortira très bien et sera beaucoup plus efficace que n'importe quelle chaîne de caractères que vous pourriez indiquer et qui sera fausse dans six mois,
\item \item
Inutile de décrire quelque chose qui est évident; documenter la méthode \mintinline{python}{get_age()} d'une personne n'aura pas beaucoup d'intérêt Inutile de décrire quelque chose qui est évident; documenter la méthode \mintinline{python}{get_age()} d'une personne n'aura pas beaucoup d'intérêt
@ -223,10 +223,10 @@ Il existe plusieurs types de balisages reconnus/approuvés:
\subsection{PEP 257} \subsection{PEP 257}
La \href{https://peps.python.org/pep-0257/#what-is-a-docstring}{PEP-257} nous donne des recommandations haut-niveau concernant la structure des docstrings: ce qu'elles doivent contenir et comment l'expliciter, sans imposer quelle que mise en forme que ce soit. La \href{https://peps.python.org/pep-0257/#what-is-a-docstring}{PEP-257} nous donne des recommandations haut-niveau concernant la structure des docstrings: ce qu'elles doivent contenir et comment l'expliciter, sans imposer quelle que mise en forme que ce soit.
de contenu, mais pas de forme, notamment sur la manière de représenter des docstrings ne nécessitant qu'une seule ligne, nécessitant plusieurs lignes ou de gérer l'indentation. de contenu, mais pas de forme, notamment sur la manière de représenter des docstrings ne nécessitant qu'une seule ligne, nécessitant plusieurs lignes ou de gérer l'indentation.
Son objectif est d'arriver à une forme de cohérence lorsqu'un utilisateur souhaitera accéder à la propriété Son objectif est d'arriver à une forme de cohérence lorsqu'un utilisateur souhaitera accéder à la propriété
Elle contient des conventions, pas des règles ou Elle contient des conventions, pas des règles ou
\begin{quote} \begin{quote}
“A universal convention supplies all of maintainability, clarity, consistency, and a foundation for good programming habits too. What it doesnt do is insist that you follow it against your will. Thats Python!” “A universal convention supplies all of maintainability, clarity, consistency, and a foundation for good programming habits too. What it doesnt do is insist that you follow it against your will. Thats Python!”
@ -249,7 +249,7 @@ A remplir
\subsection{Napoleon} \subsection{Napoleon}
Les \href{https://google.github.io/styleguide/pyguide.html\#38-comments-and-docstrings}{conventions proposées par Google} nous semblent plus faciles à lire que du RestructuredText, mais sont parfois moins bien intégrées que les docstrings officiellement supportées (par exemple, \href{https://clize.readthedocs.io/en/stable/}{clize} ne reconnait que du RestructuredText; \href{https://docs.djangoproject.com/en/stable/ref/contrib/admin/admindocs/}{l'auto-documentation} de Django également). Les \href{https://google.github.io/styleguide/pyguide.html\#38-comments-and-docstrings}{conventions proposées par Google} nous semblent plus faciles à lire que du RestructuredText, mais sont parfois moins bien intégrées que les docstrings officiellement supportées (par exemple, \href{https://clize.readthedocs.io/en/stable/}{clize} ne reconnait que du RestructuredText; \href{https://docs.djangoproject.com/en/stable/ref/contrib/admin/admindocs/}{l'auto-documentation} de Django également).
L'exemple donné dans les guides de style de Google est celui-ci: L'exemple donné dans les guides de style de Google est celui-ci:
@ -257,30 +257,30 @@ L'exemple donné dans les guides de style de Google est celui-ci:
\begin{minted}{python} \begin{minted}{python}
def fetch_smalltable_rows(table_handle: smalltable.Table, keys: Sequence[Union[bytes, str]], require_all_keys: bool = False,) -> Mapping[bytes, Tuple[str]]: def fetch_smalltable_rows(table_handle: smalltable.Table, keys: Sequence[Union[bytes, str]], require_all_keys: bool = False,) -> Mapping[bytes, Tuple[str]]:
"""Fetches rows from a Smalltable. """Fetches rows from a Smalltable.
Retrieves rows pertaining to the given keys from the Table instance Retrieves rows pertaining to the given keys from the Table instance
represented by table_handle. String keys will be UTF-8 encoded. represented by table_handle. String keys will be UTF-8 encoded.
Args: Args:
table_handle: An open smalltable.Table instance. table_handle: An open smalltable.Table instance.
keys: A sequence of strings representing the key of each table keys: A sequence of strings representing the key of each table
row to fetch. String keys will be UTF-8 encoded. row to fetch. String keys will be UTF-8 encoded.
require_all_keys: Optional; If require_all_keys is True only require_all_keys: Optional; If require_all_keys is True only
rows with values set for all keys will be returned. rows with values set for all keys will be returned.
Returns: Returns:
A dict mapping keys to the corresponding table row data A dict mapping keys to the corresponding table row data
fetched. Each row is represented as a tuple of strings. For fetched. Each row is represented as a tuple of strings. For
example: example:
{b'Serak': ('Rigel VII', 'Preparer'), {b'Serak': ('Rigel VII', 'Preparer'),
b'Zim': ('Irk', 'Invader'), b'Zim': ('Irk', 'Invader'),
b'Lrrr': ('Omicron Persei 8', 'Emperor')} b'Lrrr': ('Omicron Persei 8', 'Emperor')}
Returned keys are always bytes. If a key from the keys argument is Returned keys are always bytes. If a key from the keys argument is
missing from the dictionary, then that row was not found in the missing from the dictionary, then that row was not found in the
table (and require_all_keys must have been False). table (and require_all_keys must have been False).
Raises: Raises:
IOError: An error occurred accessing the smalltable. IOError: An error occurred accessing the smalltable.
""" """
@ -371,13 +371,13 @@ code pas très propre et qui ne sert à rien:
\begin{minted}{python} \begin{minted}{python}
from datetime import datetime from datetime import datetime
"""On stocke la date du jour dans la variable ToD4y""" """On stocke la date du jour dans la variable ToD4y"""
ToD4y = datetime.today() ToD4y = datetime.today()
def print_today(ToD4y): def print_today(ToD4y):
today = ToD4y today = ToD4y
print(ToD4y) print(ToD4y)
def GetToday(): def GetToday():
return ToD4y return ToD4y
@ -444,7 +444,7 @@ test.py:16:10: E0602: Undefined variable 'Get_Today' (undefined-variable)
Your code has been rated at -5.45/10 Your code has been rated at -5.45/10
\end{verbatim} \end{verbatim}
En gros, j'ai programmé comme une grosse bouse anémique (et oui: le score d'évaluation du code permet d'aller en négatif). En gros, j'ai programmé comme une grosse bouse anémique (et oui: le score d'évaluation du code permet d'aller en négatif).
En vrac, nous trouvons des problèmes liés: En vrac, nous trouvons des problèmes liés:
\begin{itemize} \begin{itemize}
@ -472,19 +472,19 @@ chaque code possède sa signification:
\item \item
\textbf{E} pour les erreurs ou des bugs probablement présents dans le code \textbf{E} pour les erreurs ou des bugs probablement présents dans le code
\item \item
\textbf{F} pour les erreurs internes au fonctionnement de pylint, qui font que le traitement n'a pas pu aboutir. \textbf{F} pour les erreurs internes au fonctionnement de pylint, qui font que le traitement n'a pas pu aboutir.
\end{itemize} \end{itemize}
Connaissant ceci, il est extrêmement pratique d'intégrer pylint au niveau des processus d'intégration continue, puisque la présence d'une Connaissant ceci, il est extrêmement pratique d'intégrer pylint au niveau des processus d'intégration continue, puisque la présence d'une
Pylint propose également une option particulièrement efficace, qui prend le paramètre \texttt{--errors-only}, et qui n'affiche que les occurrences appartenant à la catégorie \textbf{E}. Pylint propose également une option particulièrement efficace, qui prend le paramètre \texttt{--errors-only}, et qui n'affiche que les occurrences appartenant à la catégorie \textbf{E}.
Si nous souhaitons ignorer l'une de ces catégories, ce doit être fait explicitement: de cette manière, nous marquons notre approbation pour que pylint ignore consciemment un élément en particulier. Si nous souhaitons ignorer l'une de ces catégories, ce doit être fait explicitement: de cette manière, nous marquons notre approbation pour que pylint ignore consciemment un élément en particulier.
Cet élément peut être: Cet élément peut être:
\begin{enumerate} \begin{enumerate}
\item \textbf{Une ligne de code} \item \textbf{Une ligne de code}
\item \textbf{Un bloc de code} - une fonction, une méthode, une classe, un module, ... \item \textbf{Un bloc de code} - une fonction, une méthode, une classe, un module, ...
\item \textbf{Un projet entier}, en spécifiant la non-prise en compte au niveau du fichier \texttt{.pylintrc}, qui contient \item \textbf{Un projet entier}, en spécifiant la non-prise en compte au niveau du fichier \texttt{.pylintrc}, qui contient
\end{enumerate} \end{enumerate}
\subsection{Ignorer une ligne de code} \subsection{Ignorer une ligne de code}
@ -496,10 +496,10 @@ Cet élément peut être:
\section{Formatage de code} \section{Formatage de code}
Nous avons parlé ci-dessous de style de codage pour Python (PEP8), de style de rédaction pour la documentation (PEP257), d'un vérificateur pour nous indiquer quels morceaux de code doivent absolument être revus, ... Nous avons parlé ci-dessous de style de codage pour Python (PEP8), de style de rédaction pour la documentation (PEP257), d'un vérificateur pour nous indiquer quels morceaux de code doivent absolument être revus, ...
Reste que ces tâches sont parfois (très) souvent fastidieuses: écrire un code propre et systématiquement cohérent est une tâche ardue. Reste que ces tâches sont parfois (très) souvent fastidieuses: écrire un code propre et systématiquement cohérent est une tâche ardue.
Heureusement, il existe plusieurs outils pour nous aider au niveau du formatage automatique. Heureusement, il existe plusieurs outils pour nous aider au niveau du formatage automatique.
Même si elle n'est pas parfaite, la librairie \href{https://black.readthedocs.io/en/stable/}{Black} arrive à un très bon compromis entre Même si elle n'est pas parfaite, la librairie \href{https://black.readthedocs.io/en/stable/}{Black} arrive à un très bon compromis entre
\begin{itemize} \begin{itemize}
\item Clarté du code \item Clarté du code
@ -508,8 +508,8 @@ Même si elle n'est pas parfaite, la librairie \href{https://black.readthedocs.i
\end{itemize} \end{itemize}
Est-ce que ce formatage est idéal et accepté par tout le monde ? Est-ce que ce formatage est idéal et accepté par tout le monde ?
\textbf{Non}. \textbf{Non}.
Même Pylint arrivera parfois à râler. Même Pylint arrivera parfois à râler.
Mais ce formatage conviendra dans 97,83\% des cas (au moins). Mais ce formatage conviendra dans 97,83\% des cas (au moins).
\begin{quote} \begin{quote}
@ -535,7 +535,7 @@ vous concentrer sur le contenu}".
\section{Typage statique \index{PEP585}} \section{Typage statique \index{PEP585}}
Nous vous disions ci-dessus que Python est un langage dynamique interprété. Nous vous disions ci-dessus que Python est un langage dynamique interprété.
Concrètement, cela signifie que des erreurs qui auraient pu avoir été détectées lors de la phase de compilation, ne le sont pas avec Python. Concrètement, cela signifie que des erreurs qui auraient pu avoir été détectées lors de la phase de compilation, ne le sont pas avec Python.
Il existe cependant une solution à ce problème, sous la forme de \href{http://mypy-lang.org/}{Mypy}, qui peut vérifier une forme de typage statique de votre code source, grâce à une expressivité du code, basée sur des annotations. Il existe cependant une solution à ce problème, sous la forme de \href{http://mypy-lang.org/}{Mypy}, qui peut vérifier une forme de typage statique de votre code source, grâce à une expressivité du code, basée sur des annotations.
@ -545,10 +545,10 @@ Ces vérifications se présentent de la manière suivante:
\begin{listing}[H] \begin{listing}[H]
\begin{minted}{python} \begin{minted}{python}
from typing import List from typing import List
def first_int_elem(l: List[int]) -> int: def first_int_elem(l: List[int]) -> int:
return l[0] if l else None return l[0] if l else None
if __name__ == "__main__": if __name__ == "__main__":
print(first_int_elem([1, 2, 3])) print(first_int_elem([1, 2, 3]))
print(first_int_elem(['a', 'b', 'c'])) print(first_int_elem(['a', 'b', 'c']))
@ -561,7 +561,7 @@ Est-ce que le code ci-dessous fonctionne correctement ? \textbf{Oui}:
\begin{listing}[H] \begin{listing}[H]
\begin{verbatim} \begin{verbatim}
>>> python mypy-test.py >>> python mypy-test.py
1 1
a a
\end{verbatim} \end{verbatim}
\end{listing} \end{listing}
@ -569,7 +569,7 @@ Est-ce que le code ci-dessous fonctionne correctement ? \textbf{Oui}:
Malgré que nos annotations déclarent une liste d'entiers, rien ne nous empêche de lui envoyer une liste de caractères, sans que cela ne lui pose de problèmes. Malgré que nos annotations déclarent une liste d'entiers, rien ne nous empêche de lui envoyer une liste de caractères, sans que cela ne lui pose de problèmes.
La signature de notre fonction n'est donc pas cohérente avec son comportement. La signature de notre fonction n'est donc pas cohérente avec son comportement.
Est-ce que Mypy va râler ? \textbf{Oui, aussi}. Est-ce que Mypy va râler ? \textbf{Oui, aussi}.
Non seulement nous retournons la valeur \texttt{None} si la liste est vide alors que nous lui annoncions un entier en sortie, mais en plus, nous l'appelons avec une liste de caractères, alors que nous nous attendons à une liste d'entiers: Non seulement nous retournons la valeur \texttt{None} si la liste est vide alors que nous lui annoncions un entier en sortie, mais en plus, nous l'appelons avec une liste de caractères, alors que nous nous attendons à une liste d'entiers:
\begin{listing}[H] \begin{listing}[H]
@ -595,7 +595,7 @@ Pour corriger ceci, nous devons:
\section{Tests unitaires} \section{Tests unitaires}
Comme tout bon \textbf{langage de programmation moderne} qui se respecte, Python embarque tout un environnement facilitant le lancement de tests; Comme tout bon \textbf{langage de programmation moderne} qui se respecte, Python embarque tout un environnement facilitant le lancement de tests;
Une bonne pratique (parfois discutée) consiste cependant à switcher vers \texttt{pytest}, qui présente quelques avantages par rapport au module \texttt{unittest}: Une bonne pratique (parfois discutée) consiste cependant à switcher vers \texttt{pytest}, qui présente quelques avantages par rapport au module \texttt{unittest}:
\begin{itemize} \begin{itemize}
@ -624,7 +624,7 @@ complètement biesse; on est sur la partie théorique ici):
\end{minted} \end{minted}
\end{listing} \end{listing}
Forcément, cela va planter. Forcément, cela va planter.
Pour nous en assurer (dès fois que quelqu'un en doute), il nous suffit de démarrer la commande \texttt{pytest}: Pour nous en assurer (dès fois que quelqu'un en doute), il nous suffit de démarrer la commande \texttt{pytest}:
\begin{listing} \begin{listing}
@ -654,9 +654,14 @@ Pour nous en assurer (dès fois que quelqu'un en doute), il nous suffit de déma
Avec \texttt{pytest}, il convient d'utiliser le paquet \href{https://pypi.org/project/pytest-cov/}{\texttt{pytest-cov}}, suivi de la commande \texttt{pytest\ -\/-cov=gwift\ tests/}. Avec \texttt{pytest}, il convient d'utiliser le paquet \href{https://pypi.org/project/pytest-cov/}{\texttt{pytest-cov}}, suivi de la commande \texttt{pytest\ -\/-cov=gwift\ tests/}.
Si vous préférez rester avec le cadre de tests de Django, vous pouvez passer par le paquet\href{https://pypi.org/project/django-coverage-plugin/}{django-coverage-plugin}. Si vous préférez rester avec le cadre de tests de Django, vous pouvez passer par le paquet\href{https://pypi.org/project/django-coverage-plugin/}{django-coverage-plugin}.
Ajoutez-le dans le fichier \texttt{requirements/base.txt}, et lancez une couverture de code grâce à la commande \texttt{coverage}. Ajoutez-le dans le fichier \texttt{requirements/base.txt}, et lancez une couverture de code grâce à la commande \texttt{coverage}.
La configuration peut se faire dans un fichier \texttt{.coveragerc} que vous placerez à la racine de votre projet, et qui sera lu lors de l'exécution. La configuration peut se faire dans un fichier \texttt{.coveragerc} que vous placerez à la racine de votre projet, et qui sera lu lors de l'exécution.
\section{Gestion des exceptions}
Certains bogues causent des erreurs de mal-fonctionnement sont dus à une mauvaise conception endormie, qui ne se présente que dans certains cas très spécifiques, entourés d'une contexte inhabituel \cite[p. 9]{data_design}.
Il est primordial de gérer correctement ses exceptions, et de faire en sorte que celles qui peuvent être anticipées le soient dès la phase de développement.
\section{Gestion des versions de l'interpréteur} \section{Gestion des versions de l'interpréteur}
\begin{verbatim} \begin{verbatim}
@ -665,17 +670,17 @@ La configuration peut se faire dans un fichier \texttt{.coveragerc} que vous pla
\section{Matrice de compatibilité} \section{Matrice de compatibilité}
Une matrice de compatibilité consiste à spécifier un ensemble de plusieurs versions d'un même interpréteur (ici, Python), afin de s'assurer que votre application continue à fonctionner. Une matrice de compatibilité consiste à spécifier un ensemble de plusieurs versions d'un même interpréteur (ici, Python), afin de s'assurer que votre application continue à fonctionner.
Nous sommes donc un cran plus haut que la spécification des versions des librairies, puisque nous nous situons directement au niveau de l'interpréteur. Nous sommes donc un cran plus haut que la spécification des versions des librairies, puisque nous nous situons directement au niveau de l'interpréteur.
L'objectif consiste à définir un tableau à deux dimensions, dans lequel nous trouverons la compatibilité entre notre application et une version de l'interpréteur. L'objectif consiste à définir un tableau à deux dimensions, dans lequel nous trouverons la compatibilité entre notre application et une version de l'interpréteur.
\begin{center} \begin{center}
\begin{tabular}{|c|c|c|c|} \begin{tabular}{|c|c|c|c|}
& py37 & py38 & py39 \\ & py37 & py38 & py39 \\
\hline \hline
lib2 & V & V & V \\ lib2 & V & V & V \\
lib3 & X & V & V \\ lib3 & X & V & V \\
lib4 & X & V & V lib4 & X & V & V
\end{tabular} \end{tabular}
\end{center} \end{center}
@ -703,7 +708,7 @@ L'outil le plus connu est \href{https://tox.readthedocs.io/en/latest/}{Tox}, qui
Démarrez ensuite la commande \texttt{tox}, pour démarrer la commande \texttt{pytest} sur les environnements Python 3.6, 3.7, 3.8 et 3.9, après avoir installé nos dépendances présentes dans le fichier \texttt{requirements/dev.txt}. Démarrez ensuite la commande \texttt{tox}, pour démarrer la commande \texttt{pytest} sur les environnements Python 3.6, 3.7, 3.8 et 3.9, après avoir installé nos dépendances présentes dans le fichier \texttt{requirements/dev.txt}.
Pour que la commande ci-dessus fonctionne correctement, il sera nécessaire que vous ayez les différentes versions d'interpréteurs installées. Pour que la commande ci-dessus fonctionne correctement, il sera nécessaire que vous ayez les différentes versions d'interpréteurs installées.
Ci-dessus, la commande retournera une erreur pour chaque version non trouvée, avec une erreur type Ci-dessus, la commande retournera une erreur pour chaque version non trouvée, avec une erreur type
\texttt{ERROR:\ \ \ pyXX:\ InterpreterNotFound:\ pythonX.X}. \texttt{ERROR:\ \ \ pyXX:\ InterpreterNotFound:\ pythonX.X}.
@ -716,24 +721,24 @@ Décrire le fichier setup.cfg.
\section{Makefile} \section{Makefile}
Pour gagner un peu de temps, n'hésitez pas à créer un fichier \texttt{Makefile} que vous placerez à la racine du projet. Pour gagner un peu de temps, n'hésitez pas à créer un fichier \texttt{Makefile} que vous placerez à la racine du projet.
L'exemple ci-dessous permettra, grâce à la commande \texttt{make\ coverage}, d'arriver au même résultat que ci-dessus: L'exemple ci-dessous permettra, grâce à la commande \texttt{make\ coverage}, d'arriver au même résultat que ci-dessus:
\begin{listing} \begin{listing}
\begin{verbatim} \begin{verbatim}
# Makefile for gwift # Makefile for gwift
# #
# User-friendly check for coverage # User-friendly check for coverage
ifeq ($(shell which coverage >/dev/null 2>&1; echo $$?), 1) ifeq ($(shell which coverage >/dev/null 2>&1; echo $$?), 1)
$(error The 'coverage' command was not found. Make sure you have coverage installed) $(error The 'coverage' command was not found. Make sure you have coverage installed)
endif endif
.PHONY: help coverage .PHONY: help coverage
help: help:
@echo " coverage to run coverage check of the source files." @echo " coverage to run coverage check of the source files."
coverage: coverage:
coverage run --source='.' manage.py test; coverage report; coverage html; coverage run --source='.' manage.py test; coverage report; coverage html;
@echo "Testing of coverage in the sources finished." @echo "Testing of coverage in the sources finished."
@ -759,11 +764,11 @@ facteurs → Construction du fichier setup.cfg
[flake8] [flake8]
max-line-length = 100 max-line-length = 100
exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules,venv exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules,venv
[pycodestyle] [pycodestyle]
max-line-length = 100 max-line-length = 100
exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules,venv exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules,venv
[mypy] [mypy]
python_version = 3.8 python_version = 3.8
check_untyped_defs = True check_untyped_defs = True
@ -772,14 +777,14 @@ facteurs → Construction du fichier setup.cfg
warn_redundant_casts = True warn_redundant_casts = True
warn_unused_configs = True warn_unused_configs = True
plugins = mypy_django_plugin.main plugins = mypy_django_plugin.main
[mypy.plugins.django-stubs] [mypy.plugins.django-stubs]
django_settings_module = config.settings.test django_settings_module = config.settings.test
[mypy-*.migrations.*] [mypy-*.migrations.*]
# Django migrations should not produce any errors: # Django migrations should not produce any errors:
ignore_errors = True ignore_errors = True
[coverage:run] [coverage:run]
include = khana/* include = khana/*
omit = *migrations*, *tests* omit = *migrations*, *tests*

View File

@ -1,4 +1,4 @@
\chapter{Arborescences} \chapter{Arborescences et graphs}
On a un exemple de remplissage/vidage d'une closure table, mais il faudrait en fait présenter les listes adjacentes et les autres structures de données. On a un exemple de remplissage/vidage d'une closure table, mais il faudrait en fait présenter les listes adjacentes et les autres structures de données.
Comme ça on pourra introduire les graphs juste après. Comme ça on pourra introduire les graphs juste après.
@ -23,12 +23,12 @@ class Command(BaseCommand):
for entity in entities: for entity in entities:
breadcrumb = [node for node in entity.breadcrumb()] breadcrumb = [node for node in entity.breadcrumb()]
tree = set(EntityTreePath.objects.filter(descendant=entity)) tree = set(EntityTreePath.objects.filter(descendant=entity))
for idx, node in enumerate(breadcrumb): for idx, node in enumerate(breadcrumb):
tree_path, _ = EntityTreePath.objects.get_or_create( tree_path, _ = EntityTreePath.objects.get_or_create(
ancestor=node, descendant=entity, weight=idx + 1 ancestor=node, descendant=entity, weight=idx + 1
) )
if tree_path in tree: if tree_path in tree:
tree.remove(tree_path) tree.remove(tree_path)
for tree_path in tree: for tree_path in tree:

View File

@ -9,7 +9,6 @@
\usepackage{setspace} \usepackage{setspace}
\usepackage{listing} \usepackage{listing}
\usepackage{minted} \usepackage{minted}
\usepackage{ulem}
\usepackage{graphics} \usepackage{graphics}
\usepackage{float} \usepackage{float}
\usepackage[export]{adjustbox} \usepackage[export]{adjustbox}
@ -62,7 +61,6 @@
\include{chapters/heroku.tex} \include{chapters/heroku.tex}
\include{chapters/kubernetes.tex} \include{chapters/kubernetes.tex}
\include{chapters/deployment-tools.tex} \include{chapters/deployment-tools.tex}
\include{chapters/deployment-processes.tex}
\chapter{Conclusions} \chapter{Conclusions}
\include{parts/principles.tex} \include{parts/principles.tex}
@ -83,8 +81,8 @@
\include{chapters/api.tex} \include{chapters/api.tex}
\include{chapters/filters.tex} \include{chapters/filters.tex}
\include{chapters/trees.tex} \include{chapters/trees.tex}
\include{chapters/graphs.tex}
\include{chapters/i18n.tex} \include{chapters/i18n.tex}
\include{chapters/deployment-processes.tex}
\chapter{Conclusions} \chapter{Conclusions}

View File

@ -6,28 +6,28 @@ Make it work, make it right, make it fast
--- Kent Beck --- Kent Beck
\end{quote} \end{quote}
En fonction de vos connaissances et compétences, la création d'une nouvelle application est une étape relativement facile à mettre en place. En fonction de vos connaissances et compétences, la création d'une nouvelle application est une étape relativement facile à mettre en place.
Le code qui permet de faire tourner cette application peut ne pas être élégant, voire buggé jusqu'à la moëlle, il pourra fonctionner et faire "preuve de concept". Le code qui permet de faire tourner cette application peut ne pas être élégant, voire buggé jusqu'à la moëlle, il pourra fonctionner et faire "preuve de concept".
Les problèmes arriveront lorsqu'une nouvelle demande sera introduite, lorsqu'un bug sera découvert et devra être corrigé ou lorsqu'une dépendance cessera de fonctionner ou d'être disponible. Les problèmes arriveront lorsqu'une nouvelle demande sera introduite, lorsqu'un bug sera découvert et devra être corrigé ou lorsqu'une dépendance cessera de fonctionner ou d'être disponible.
Or, une application qui n'évolue pas, meurt. Or, une application qui n'évolue pas, meurt.
Toute application est donc destinée, soit à être modifiée, corrigée et suivie, soit à déperrir et à être Toute application est donc destinée, soit à être modifiée, corrigée et suivie, soit à déperrir et à être
délaissée par ses utilisateurs. délaissée par ses utilisateurs.
Et c'est juste cette maintenance qui est difficile. Et c'est juste cette maintenance qui est difficile.
L'application des principes présentés et agrégés ci-dessous permet surtout de préparer correctement tout ce qui pourra arriver, sans aller jusqu'au « \textbf{You Ain't Gonna Need It} » (ou \textbf{YAGNI\index{YAGNI}}), qui consiste à surcharger tout développement avec des fonctionnalités non demandées, juste « au cas ou ». L'application des principes présentés et agrégés ci-dessous permet surtout de préparer correctement tout ce qui pourra arriver, sans aller jusqu'au « \textbf{You Ain't Gonna Need It} » (ou \textbf{YAGNI\index{YAGNI}}), qui consiste à surcharger tout développement avec des fonctionnalités non demandées, juste « au cas ou ».
Pour paraphraser une partie de l'introduction du livre \emph{Clean Architecture} \cite{clean_architecture}: Pour paraphraser une partie de l'introduction du livre \emph{Clean Architecture} \cite{clean_architecture}:
\begin{quote} \begin{quote}
Getting software right is hard: it takes knowledge and skills that most young programmers don't take the time to develop. Getting software right is hard: it takes knowledge and skills that most young programmers don't take the time to develop.
It requires a level of discipline and dedication that most programmers never dreamed they'd need. It requires a level of discipline and dedication that most programmers never dreamed they'd need.
Mostly, it takes a passion for the craft and the desire to be a professional. Mostly, it takes a passion for the craft and the desire to be a professional.
--- Robert C. Martin Clean Architecture --- Robert C. Martin Clean Architecture
\end{quote} \end{quote}
Le développement d'un logiciel nécessite une rigueur d'exécution et des connaissances précises dans des domaines extrêmement variés. Le développement d'un logiciel nécessite une rigueur d'exécution et des connaissances précises dans des domaines extrêmement variés.
Il nécessite également des intentions, des (bonnes) décisions et énormément d'attention. Il nécessite également des intentions, des (bonnes) décisions et énormément d'attention.
Indépendamment de l'architecture que vous aurez choisie, des technologies que vous aurez patiemment évaluées et mises en place, une architecture et une solution peuvent être cassées en un instant, en même temps que tout ce que vous aurez construit, dès que vous en aurez détourné le regard. Indépendamment de l'architecture que vous aurez choisie, des technologies que vous aurez patiemment évaluées et mises en place, une architecture et une solution peuvent être cassées en un instant, en même temps que tout ce que vous aurez construit, dès que vous en aurez détourné le regard.
Un des objectifs ici est de placer les barrières et les gardes-fous (ou Un des objectifs ici est de placer les barrières et les gardes-fous (ou
@ -55,7 +55,6 @@ Dans une version plus manuelle, cela pourrait se résumer à ces trois
étapes (la dernière étant formellement facultative): étapes (la dernière étant formellement facultative):
\begin{enumerate} \begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\item \item
Démarrer un script, Démarrer un script,
\item \item
@ -66,10 +65,4 @@ Dans une version plus manuelle, cela pourrait se résumer à ces trois
technologie existe encore\ldots\hspace{0pt}). technologie existe encore\ldots\hspace{0pt}).
\end{enumerate} \end{enumerate}
\begin{quote} Sans aller jusqu'à demander de développer vos algorithmes sur douze pieds, la programmation reste un art régit par un ensemble de bonnes pratiques, par des règles à respecter et par la nécessité de travailler avec d'autres personnes qui ont souvent une expérience, des compétences ou une approche différente.
La poésie est "l'art d'évoquer et de suggérer les sensations, les impressions, les émotions les plus vives par l'union intense des sons, des rythmes, des harmonies, en particulier par les vers."
-- https://www.larousse.fr/dictionnaires/francais/po%C3%A9sie/61960
\end{quote}
Sans aller jusqu'à demander de développer vos algorithmes sur douze pieds, la programmation reste un art régit par un ensemble de bonnes pratiques, par des règles à respecter et par la nécessité de travailler avec d'autres personnes qui ont \sout{parfois} souvent une expérience, des compétences ou une approche différente.

View File

@ -1,15 +1,15 @@
\part{Services Oriented Applications} \part{Services Oriented Applications}
Nous avons fait exprès de reprendre l'acronyme d'une \emph{Services Oriented Architecture} pour cette partie. L'objectif est de vous mettre Nous avons fait exprès de reprendre l'acronyme d'une \emph{Services Oriented Architecture} pour cette partie.
la puce à l'oreille quant à la finalité du développement: que l'utilisateur soit humain, bot automatique ou client Web, l'objectif est de fournir des applications résilientes, disponibles et accessibles.
Dans cette partie, nous aborderons les vues, la mise en forme, la mise en page, la définition d'une interface REST, la définition d'une interface GraphQL et le routage d'URLs.
Dans cette partie, en page, la définition d'une interface REST, la définition d'une interface GraphQL et le routage d'URLs.
\begin{quote} \begin{quote}
Don't make me think, or why I switched from JS SPAs to Ruby On Rails Don't make me think, or why I switched from JS SPAs to Ruby On Rails
\url{https://news.ycombinator.com/item?id=30206989\&utm_term=comment} \url{https://news.ycombinator.com/item?id=30206989\&utm_term=comment}
\end{quote} \end{quote}
On a parcouru les templates et le mode "monolithique de DJango". On a parcouru les templates et le mode "monolithique de DJango".