Package corelibs

Note

Bienvenue dans la documentation de corelibs.

L’objectif de corelibs est d’agréger dans différents modules, toutes les fonctionnalités utiles pour simplifier nos travaux sans devoir tout réécrire à chaque fois…

Installation & Mise à jour

Note

corelibs est orienté Anaconda, sur les autres environnements, corelibs reste fonctionnel, sauf toutes les spécificités propre à l’environnement Anaconda qui resteront sans réponse.

Warning

corelibs gère toutes les dépendances lors de la phase installation/mise à jour.
Malgré tout, certains packages peuvent ne plus être accessibles (changement de dépôt/adresse…) il faudrait alors faire l’installation manuellement, comme c’est le cas pour le package mkl-service (au moment de la rédaction de ce présent document)

Description générale Installation

Étapes pour installer le package corelibs:

  1. lancer le terminal Anaconda ayant pour titre Anaconda Prompt (Anaconda3)

    (base) C:\Users\kim>
    
  2. dans Anaconda Prompt (Anaconda3) :

    1. si besoin, lister les environnements existant

      (base) C:\Users\kim>conda env list
      
    2. activer l’environnement sur lequel est souhaité l’installation du package corelibs

      (base) C:\Users\kim>conda activate nom_environnement
      
    3. installer le package mkl-service récalcitrant, via le dépôt conda, canal conda-forge.

      (nom_environnement) C:\Users\kim>conda install -c conda-forge mkl-service
      
    4. installer le package corelibs

      (nom_environnement) C:\Users\kim>pip install corelibs
      

Description générale Mise à jour

Étapes pour mettre à jour le package corelibs:

  1. lancer le terminal Anaconda et dans Anaconda Prompt (Anaconda3)

  2. activer l’environnement sur lequel est installé le package corelibs

    (base) C:\Users\kim>conda activate nom_environnement
    
  3. mettre à jour corelibs

    (nom_environnement) C:\Users\kim>pip install corelibs -U
    

Interface Utilisateur

Description générale

corelibs a une interface utilisateur pour accéder plus rapidement à la documentation et certaines fonctionnalités, pour cela :

  1. lancer le terminal Anaconda et dans Anaconda Prompt (Anaconda3)

  2. activer l’environnement sur lequel est installé le package corelibs

    (base) C:\Users\kim>conda activate nom_environnement
    
  3. lancer l’interface corelibs avec la commande

    (nom_environnement) C:\Users\kim>corelibs
    

qui affichera l’interface suivante

_images/corelibs_gui.png

Configurations

Description générale

Ci-dessous sont listés l’ensemble des constantes définies et utilisées dans le package corelibs.

Ces constantes peuvent être utilisées telles que définies ou écrasées à discrétion :
  • soit de manière globale, via le fichier user_config.py

  • soit localement, dans les programmes python appelants.

L’ordre de recherche d’une constante est donc :
  1. localement

  2. user_config.py

  3. config.py (le fichier de configuration de corelibs)

Dans le cas d’une constante simple, par exemple DEFAULT_LOGS_EXTENSION = ".log", corelibs utilisera la version écrasée telle que redéfinie.

Dans le cas d’une constante plus complexe, de type dictionnaire, par exemple

DEFAULT_FIELD_STYLES = {
"asctime": {"color": 242, "bright": True},
"hostname": {"color": "magenta"},
"username": {"color": "yellow"},
"levelname": {"color": 242, "bright": True},
"name": {"color": "blue"},
"programname": {"color": "cyan"}
}

corelibs ne remplacera que les clés/valeurs redéfinies (i.e. les autres clés/valeurs seront gardées inchangées)

# Exemple instructions pour utiliser le fichier config utilisateur de manière globale et locale (portée au sein du programme actif)

import user_config as uc

print(uc.MA_CONSTANTE_UTILISATEUR)  # affichera "Hello! =}"

# écrasement des constantes utilisateurs, avec une portée locale
uc.MA_CONSTANTE_UTILISATEUR = "Hello Kim ❤ =}"
print(uc.MA_CONSTANTE_UTILISATEUR)  # affiche "Hello Kim ❤ =}"

# idem pour les constantes corelibs (attention à lire les documentations officielles tiers pour ne pas tout casser...)
DEFAULT_MIN_BYTE_SIZE_FORMAT = {  # définition locale
    "octet": {"min_size": 0},
    "Ko": {"min_size": 1},
    "Mo": {"min_size": 1},
    "Go": {"min_size": 1},
    "To": {"min_size": 1}
}
print(DEFAULT_MIN_BYTE_SIZE_FORMAT)  # affiche {'octet': {'min_size': 0}, 'Ko': {'min_size': 1}, 'Mo': {'min_size': 1}, 'Go': {'min_size': 1}, 'To': {'min_size': 1}}

print(uc.DEFAULT_MIN_BYTE_SIZE_FORMAT)  # {'octet': {'min_size': 0}, 'Ko': {'min_size': 1}, 'Mo': {'min_size': 1}, 'Go': {'min_size': 1}, 'To': {'min_size': 0.5}}

# Le fichier user_config.py se trouve dans le dossier caché nommé ".corelibs" se trouvant à :
#   • %HOMEPATH%/.corelibs sous Windows
#   • ~/.corelibs sous Linux
#
# accessible manuellement ou via l'interface graphique Corelibs avec la commande terminale
#    $ corelibs
#
# ce fichier de configuration est modifiable à discrétion avec en particulier, la possibilité de rajouter des variables utilisateurs propres globalement vu par n'importe quel programme qui en fait l'import

Note

Le fichier user_config.py se trouve à l’emplacement %HOMEPATH%/.corelibs (pour les utilisateurs Windows) et ~/.corelibs (pour les utilisateurs Linux/Unix) - cf. corelibs.lazy.open_explorer()
Ce fichier est automatiquement inclut à l’exécution, ainsi que son dossier parent. De ce fait, il est donc possible d’y définir des constantes utilisateurs et/ou d’ajouter dans le répertoire parent des programmes python devant être inclus et exécuté globalement.
La configuration fonctionne également sur Jupyter
_images/jupyter_user_config.png

Warning

Les écrasements sont contrôlés par des schémas de validation. Si le fichier est corrompu, il est possible de supprimer le fichier user_config.py. corelibs le regénèrera automatiquement.
Pour plus de détails sur les valeurs possibles, suivre la documentation officielle des packages tiers (cf. Liens utiles et Dépendances).
import logging as log
########################################################################################################################
# Bienvenue à...                                                                                                       #
#                                      ______                ___ __                                                    #
#                                     / ____/___  ________  / (_) /_  _____                                            #
#                                    / /   / __ \/ ___/ _ \/ / / __ \/ ___/                                            #
#                                   / /___/ /_/ / /  /  __/ / / /_/ (__  )                                             #
#                                   \____/\____/_/   \___/_/_/_.___/____/                              #################
#                                                                                                     # user_config.py #
########################################################################################################################


########################################################################################################################
# CONFIGURATION UTILISATEUR ############################################################################################
########################################################################################################################
# ici les nouvelles définitions/constantes utilisateur...
MA_CONSTANTE_UTILISATEUR = "Hello! =}"
########################################################################################################################
# /CONFIGURATION UTILISATEUR ###########################################################################################
########################################################################################################################


####################################
# DÉBUT CONFIGURATION CORELIBS... ###
########################################################################################################################
# CONFIGURATION INTERFACE UI ###########################################################################################
########################################################################################################################
# Chemin pour le script Conda
# Windows 10: C:\Users\<your-username>\Anaconda3\
# macOS: /Users/<your-username>/anaconda3 for the shell install, ~/opt for the graphical install.
# Linux: /home/<your-username>/anaconda3
# cf. https://docs.anaconda.com/anaconda/user-guide/faq/ pour plus de détail
UI_CONDA_PATH = r"C:\ProgramData\Anaconda3\Scripts"

# Par défaut, le thème est aléatoire, pour connaitre le nom du thème affiché, mettre à Oui
UI_DISPLAY_THEME_NAME = False

# Nom du thème à afficher
UI_THEME_NAME = "Sandy Beach"  # None pour un affichage aléatoire parmi la liste des thèmes ci-dessous
# Nom thèmes disponibles :
# "Black" "Blue Mono" "Blue Purple" "Bright Colors" "Brown Blue" "Dark" "Dark 2" "Dark Amber" "Dark Black"
# "Dark Black 1" "Dark Blue" "Dark Blue 1" "Dark Blue 2" "Dark Blue 3" "Dark Blue 4" "Dark Blue 5" "Dark Blue 6"
# "Dark Blue 7" "Dark Blue 8" "Dark Blue 9" "Dark Blue 10" "Dark Blue 11" "Dark Blue 12" "Dark Blue 13" "Dark Blue 14"
# "Dark Blue 15" "Dark Blue 16" "Dark Blue 17" "Dark Brown" "Dark Brown 1" "Dark Brown 2" "Dark Brown 3" "Dark Brown 4"
# "Dark Brown 5" "Dark Brown 6" "Dark Brown 7" "Dark Green" "Dark Green 1" "Dark Green 2" "Dark Green 3" "Dark Green 4"
# "Dark Green 5" "Dark Green 6" "Dark Green 7" "Dark Grey" "Dark Grey 1" "Dark Grey 2" "Dark Grey 3" "Dark Grey 4"
# "Dark Grey 5" "Dark Grey 6" "Dark Grey 7" "Dark Grey 8" "Dark Grey 9" "Dark Grey 10" "Dark Grey 11" "Dark Grey 12"
# "Dark Grey 13" "Dark Grey 14" "Dark Purple" "Dark Purple 1" "Dark Purple 2" "Dark Purple 3" "Dark Purple 4"
# "Dark Purple 5" "Dark Purple 6" "Dark Purple 7" "Dark Red" "Dark Red 1" "Dark Red 2" "Dark Tan Blue" "Dark Teal"
# "Dark Teal 1" "Dark Teal 2" "Dark Teal 3" "Dark Teal 4" "Dark Teal 5" "Dark Teal 6" "Dark Teal 7" "Dark Teal 8"
# "Dark Teal 9" "Dark Teal 10" "Dark Teal 11" "Dark Teal 12" "Default" "Default 1" "Default No More Nagging" "Green"
# "Green Mono" "Green Tan" "Hot Dog Stand" "Kayak" "Light Blue" "Light Blue 1" "Light Blue 2" "Light Blue 3"
# "Light Blue 4" "Light Blue 5" "Light Blue 6" "Light Blue 7" "Light Brown" "Light Brown 1" "Light Brown 2"
# "Light Brown 3" "Light Brown 4" "Light Brown 5" "Light Brown 6" "Light Brown 7" "Light Brown 8" "Light Brown 9"
# "Light Brown 10" "Light Brown 11" "Light Brown 12" "Light Brown 13" "Light Gray 1" "Light Green" "Light Green 1"
# "Light Green 2" "Light Green 3" "Light Green 4" "Light Green 5" "Light Green 6" "Light Green 7" "Light Green 8"
# "Light Green 9" "Light Green 10" "Light Grey" "Light Grey 1" "Light Grey 2" "Light Grey 3" "Light Grey 4"
# "Light Grey 5" "Light Grey 6" "Light Purple" "Light Teal" "Light Yellow" "Material 1" "Material 2" "Neutral Blue"
# "Purple" "Python" "Reddit" "Reds" "Sandy Beach" "System Default" "System Default 1" "System Default For Real" "Tan"
# "Tan Blue" "Teal Mono" "Topanga"
########################################################################################################################
# /CONFIGURATION INTERFACE UI ##########################################################################################
########################################################################################################################

########################################################################################################################
# CONFIGURATION LOCALE #################################################################################################
########################################################################################################################
# buffer de lecture/écriture en nombres d'octets
DEFAULT_BYTE_CHUNK_SIZE = 1048576 * 64  # 1048576 bytes <=> 1024KB <=> 1MB

# buffer de prévisualisation/lecture/écriture en nombre de lignes
DEFAULT_BUFFER_CHUNK_SIZE = 65536  # <=> max lignes Excel < 2007, sinon 1048576 lignes

# encodage par défaut des fichiers
DEFAULT_ENCODING_FILE = "latin-1"  # Western Europe latin-1 pour les français, sinon choisir "utf-8" ou "ISO-8859-1"

# format français par défaut
DEFAULT_LOCALE_TIME = "fr"  # en pour l'anglais => voir corelibs.lazy.get_locale_tab() pour la liste complète

# code page encoding pour les retour terminal des appels commandes DOS
DEFAULT_DOS_CMD_CP_ENCODING = "cp850"  # cf. https://en.wikipedia.org/wiki/Code_page_850

# format des tailles avec leur valeur minimale pour l'affichage automatisé
DEFAULT_MIN_BYTE_SIZE_FORMAT = {
    "octet": {"min_size": 0},
    "Ko": {"min_size": 1},
    "Mo": {"min_size": 1},
    "Go": {"min_size": 1},
    "To": {"min_size": 0.5}
}
########################################################################################################################
# /CONFIGURATION LOCALE ################################################################################################
########################################################################################################################


########################################################################################################################
# CONFIGURATION PROJET #################################################################################################
########################################################################################################################
# nom structures modèles de répertoires par défaut
DEFAULT_DIR_SCAFFOLDING = {
    "input": {  # dossier contenant toutes les données "entrées"
        "name": "__INPUTS__",
        "make": True
    },
    "output": {  # dossier contenant toutes les données "sorties"
        "name": "__OUTPUTS__",
        "make": True
    },
    "logs": {  # dossier contenant toutes les sorties "logs"
        "name": "__LOGS__",
        "make": True
    },
    "docs": {  # dossier contenant toutes les documentations/specs liées au projet
        "name": "__DOCS__",
        "make": True
    },
}
########################################################################################################################
# /CONFIGURATION PROJET ################################################################################################
########################################################################################################################


########################################################################################################################
# CONFIGURATION LOG ####################################################################################################
########################################################################################################################
# forcer l'exécution des instructions suivantes même quand une erreur est levée (Attention à son emploi...)
DEFAULT_IGNORE_ERROR = False

# affichage détaillé de la pile d'exécution
DEFAULT_STACK_TRACE = False

# redirection de l'affichage détaillé de la pile d'exécution vers la log
DEFAULT_STACK_TRACE_2_FILE = False

# affichage détaillé de la pile d'exécution
# par défaut, le style de couleur sera :
# • "lightbg" si environnement Jupyter, sinon "darkbg2" si DEFAULT_STACK_TRACE_2_FILE est faux
# • "plaintext" si DEFAULT_STACK_TRACE_2_FILE est vrai
DEFAULT_STYLE_STACK_TRACE = "default"  # valeur possibles : plaintext, color, darkbg2 ou lightbg

# contexte du code source pour les affichage détaillés des logs
DEFAULT_CONTEXT_SOURCE_LINES = 3

# affichage informations en provenance du package corelibs
DEFAULT_VERBOSE = False

# nom fichier log par défaut dans le cas des entrées standards
DEFAULT_STDIN_LOGS_NAME = "__STDIN_"

# extension par défaut des fichiers logs
DEFAULT_LOGS_EXTENSION = ".log"

# niveau d"affichage des logs, valeurs possibles :
# * log.DEBUG <=> 10
# * log.INFO <=> 20 (valeur par défaut)
# * log.WARNING <=> 30
# * log.ERROR <=> 40
# * log.CRITICAL <=> 50
#
# pour désactiver une alerte, on augmente son niveau, par exemple WARNING (30)
# et dans ce cas, les alertes de niveau DEBUG et INFO seront ignorées.
DEFAULT_LOG_LEVEL = log.INFO  # ou 20

# le style par défaut des LOG affichées dans la sortie standard
DEFAULT_FIELD_STYLES = {
    "asctime": {"color": 242, "bright": True},
    "hostname": {"color": "magenta"},
    "username": {"color": "yellow"},
    "levelname": {"color": 242, "bright": True},
    "levelno": {"color": 242, "bright": True},
    "lineno": {"color": "white"},
    "process": {"color": "white"},
    "name": {"color": "blue"},
    "module": {"color": "blue"},
    "programname": {"color": "cyan"},
    "thread": {"color": "white"},
    "filename": {"color": "blue"},
    "funcName": {"color": "blue"},
}

# les couleurs par défaut selon le niveau
DEFAULT_LEVEL_STYLES = {
    "critical": {
        "color": 255,
        "background": "red",
    },
    "error": {"color": "red"},
    "warning": {"color": "yellow"},
    "debug": {"color": "green"},
    "info": {"color": "cyan"},
    "notice": {"color": "magenta"},
    "spam": {"color": "green"},
    "success": {"color": "green"},
    "verbose": {"color": "blue"},
}

# le label par défaut de la log
# si DEFAULT_SHORT_LOG_LABEL alors affiche simplement le nom du programme qui a généré la log
# sinon affichera le chemin complet avec le nom du programme source et cible qui ont levé l"info ou l"alerte
DEFAULT_SHORT_LOG_LABEL = True

# le format par défaut de la log
DEFAULT_LOG_FORMAT = \
    "%(asctime)s %(username)s@%(hostname)s - %(name)s" \
    + " [P%(process)d - T%(thread)d - %(filename)s:%(lineno)05d] • %(levelname)13s : %(message)s"

# le timestamp par défaut de la log
DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
########################################################################################################################
# /CONFIGURATION LOG ###################################################################################################
########################################################################################################################

Module log

Description générale

Module permettant de manipuler tout ce qui est relatif à la gestion des logs

class corelibs.log.ColorLog(name=None, log_level=20, output_2_log_file=True, location=None, log_file_name=None, field_styles=None, level_styles=None, log_format=None, log_date_format=None)

Description

Classe de base pour manipuler les logs colorées sans distinctions en sortie terminal ou Jupyter Notebooks.

cf. Installation & Mise à jour pour la configuration par défaut (modifiable lors de l’instanciation de la classe)

Note

ColorLog instancie dynamiquement la classe TermColorLog ou JupyterColorLog

pour plus de détails concernant les arguments :
Parameters
  • name

    indique le nom de la log en cours

    valeur possibles: None/nom de la log
    valeur par défaut: None

  • log_level

    indique le niveau minimum d’alerte pour l’affichage des logs

    valeur possibles: None/niveau d’alerte
    valeur par défaut: config.DEFAULT_LOG_LEVEL

  • output_2_log_file

    indique s’il faut ou non écrire les logs dans un fichier en sortie

    valeur possibles: False/True
    valeur par défaut: True

  • location

    indique l’emplacement des fichiers logs de sortie

    valeur possibles: None/chemin dossier logs
    valeur par défaut: None
    • Si location non renseigné, par défaut, les logs seront enregistrés dans le dossier retourné par

      corelibs.lazy.mkdir()

    • Sinon les logs seront enregistrés dans le dossier location\DEFAULT_DIR_SCAFFOLDING[“logs”][“name”]

  • log_file_name

    indique le nom de la log

    valeur possibles: None/nom de la log
    valeur par défaut: None

  • field_styles

    permet de définir le style d’affichage des logs dans la sortie standard

    valeur possibles: None/dictionnaire
    valeur par défaut: None

  • level_styles

    permet de définir le style d’affichage des niveaux d’alerte des logs dans la sortie standard

    valeur possibles: None/dictionnaire
    valeur par défaut: None

  • log_format

    permet de définir le format d’affichage de la log

    valeur possibles: None/format d’affichage de la log
    valeur par défaut: None

  • log_date_format

    permet de définir le format d’affichage horodaté de la log

    valeur possibles: None/format d’affichage horodaté de la log
    valeur par défaut: None

Note

cf. Installation & Mise à jour :
  • DEFAULT_LOG_LEVEL

  • DEFAULT_FIELD_STYLES

  • DEFAULT_LEVEL_STYLES

  • DEFAULT_DIR_SCAFFOLDING

  • DEFAULT_LOGS_EXTENSION

  • DEFAULT_LOG_FORMAT

  • DEFAULT_LOG_DATE_FORMAT

Exemple :

# termcolorlog.py
from corelibs import config, log

# écrasement de la config par défaut de corelibs
# config.DEFAULT_VERBOSE = True
config.DEFAULT_LOGS_EXTENSION = ".LOG"
# config.DEFAULT_SHORT_LOG_LABEL = False
# config.DEFAULT_STACK_TRACE_2_FILE = True
# config.DEFAULT_STYLE_STACK_TRACE = "plaintext"
# config.DEFAULT_STACK_TRACE = True
# config.DEFAULT_CONTEXT_SOURCE_LINES = 7

# instanciation par défaut pour la sortie standard terminal seulement
cl = log.TermColorLog()

cl.debug("Bonjour, ceci est un test niveau DEBUG")
cl.info("Bonjour, ceci est un test niveau INFO", True)
cl.warning("Bonjour, ceci est un test niveau WARNING")
cl.error("Bonjour, ceci est un test niveau ERROR", trace_back=True)
cl.critical("Bonjour, ceci est un test niveau CRITIQUE")


# redéfinition du nom du dossier logs
# /!\ IMPORTANT /!\
#    il n'y a que le dossier des logs qui est pris en compte, définir les autres dossiers ici est inutile
config.DEFAULT_DIR_SCAFFOLDING = {
    "logs": {  # dossier contenant toutes les sorties "logs"
        "name": "__R2D2-LOGS__",
        "make": True
    },
}

# instanciation personnalisée
user_cl = log.TermColorLog(
    name="Ma log à moi que j'ai",
    log_level=None,
    output_2_log_file=True,
    location=r"D:\OneDrive\Documents\_TEST_\T32020",
    log_file_name=None,
    field_styles={
        "asctime": {"color": "black"},
        "name": {"color": "green"},
    },
    level_styles={
        "info": {
            "color": "white",
            "background": "cyan"
        },
    },
    log_format="%(asctime)s - %(name)s [%(filename)s:%(lineno)07d] <> %(levelname)13s : %(message)s",
    log_date_format="%A %d %B %Y"
)

user_cl.debug("Bonjour, ceci est un test personnalisé niveau DEBUG")
user_cl.info("Bonjour, ceci est un test personnalisé niveau INFO")
user_cl.warning("Bonjour, ceci est un test personnalisé niveau WARNING")
user_cl.error("Bonjour, ceci est un test personnalisé niveau ERROR", trace_back=True)
user_cl.critical("Bonjour, ceci est un test personnalisé niveau CRITIQUE", trace_back=True)


# Création log dynamiquement selon contexte terminal standard ou Jupyter Notebooks
xcl = log.ColorLog(log_file_name="KIM")
xcl.debug("Bonjour, ceci est un test dynamique niveau DEBUG")
xcl.info("Bonjour, ceci est un test dynamique niveau INFO")
xcl.warning("Bonjour, ceci est un test dynamique niveau WARNING")
xcl.error("Bonjour, ceci est un test dynamique niveau ERROR", trace_back=True)
xcl.critical("Bonjour, ceci est un test dynamique niveau CRITIQUE", trace_back=True)
class corelibs.log.ColoredFormatter(*args, colors: Optional[Dict[str, str]] = None, **kwargs)

Initialize the formatter with specified format strings.

Initialize the formatter either with the specified format string, or a default as described above. Allow for specialized date formatting with the optional datefmt argument. If datefmt is omitted, you get an ISO8601-like (or RFC 3339-like) format.

Use a style parameter of ‘%’, ‘{‘ or ‘$’ to specify that you want to use one of %-formatting, str.format() ({}) formatting or string.Template formatting in your format string.

Changed in version 3.2: Added the style parameter.

format(record) → str

Format the specified record as text.

The record’s attribute dictionary is used as the operand to a string formatting operation which yields the returned string. Before formatting the dictionary, a couple of preparatory steps are carried out. The message attribute of the record is computed using LogRecord.getMessage(). If the formatting string uses the time (as determined by a call to usesTime(), formatTime() is called to format the event time. If there is exception information, it is formatted using formatException() and appended to the message.

class corelibs.log.JupyterColorLog(log_level=20)

Description

Classe de base pour manipuler les logs colorées sur les sorties web Jupyter Notebooks.

cf. Installation & Mise à jour pour la configuration par défaut (modifiable lors de l’instanciation de la classe)

Parameters

log_level

indique le niveau minimum d’alerte pour l’affichage des logs

valeur possibles: None/niveau d’alerte
valeur par défaut: config.DEFAULT_LOG_LEVEL

Note

cf. Installation & Mise à jour :
  • DEFAULT_LOG_LEVEL

Exemple :

# jupytercolorlog.py
from corelibs import config, log

# écrasement de la config par défaut de corelibs
# config.DEFAULT_VERBOSE = True
# config.DEFAULT_STACK_TRACE = True
# config.DEFAULT_STYLE_STACK_TRACE = "color"
# config.DEFAULT_CONTEXT_SOURCE_LINES = 7

# instanciation par défaut pour Jupyter Notebooks seuleemnt
cl = log.JupyterColorLog()

cl.debug("Bonjour, ceci est un test niveau DEBUG")
cl.info("Bonjour, ceci est un test niveau INFO", trace_back=True)
cl.warning("Bonjour, ceci est un test niveau WARNING")
cl.error("Bonjour, ceci est un test niveau ERROR")
cl.critical("Bonjour, ceci est un test niveau CRITIQUE")


# Création log dynamiquement selon contexte terminal standard ou Jupyter Notebooks
xcl = log.ColorLog()

xcl.debug("Bonjour, ceci est un test dynamique niveau DEBUG")
xcl.info("Bonjour, ceci est un test dynamique niveau INFO")
xcl.warning("Bonjour, ceci est un test dynamique niveau WARNING")
xcl.error("Bonjour, ceci est un test dynamique niveau ERROR")
xcl.critical("Bonjour, ceci est un test dynamique niveau CRITIQUE")
_images/jupyter_color_log.png
class corelibs.log.StatusBars(title=None, color='bold_cyan_on_white')

Description

Classe permettant de créer une barre d’état figée pour afficher dynamiquement les différentes étapes ou processus, ainsi que des barres de progression.
Chaque barre de progression retour le temps intermédiaire passé par le processus ayant appelé cette barre de progression.
La classe StatusBars donne également le temps d’exécution total

Warning

Les barres d’états ne fonctionnent que sur des terminaux et terminaux émulés (sous PyCharm par exemple voir Run > Edit Configurations… > Execution > Emulate terminal in output console)
Parameters
  • title – indique le titre à donner à la barre d’état

  • color

    indique la couleur souhaitée pour la barre d’état

    valeur possibles: black, red, green, yellow, blue, magenta, cyan ou white (pour la couleur principale, comme pour la couleur du fond)
    • Pour chaque couleur, il y a 3 états possibles, bold, bright et normal (qui est le nom simple de la couleur)

    • Chaque couleur peut être combinée une fois avec un état cité ci-dessus, l’état doit précéder le nom de la couleur

    • La couleur principale et la couleur de fond se combinent avec le mot on

    • Les mots sont liés par le caractère underscore _

    • Par exemple bold_black_on_bright_cyan

Returns

rien…

Exemple :

# statusbars.py
# %%
import random

from corelibs import log

# /!\ ATTENTION /!\
#     Jupyter Notebook n'étant pas un terminal, il ne se passera rien... =}
# Instanciations
sb = log.StatusBars()
cl = log.ColorLog(output_2_log_file=False, log_level=10)

log_methods = {  # dictionnaire avec les adresses des différentes méthode présentes dans TermColorLog
    0: {
        "status": "success",
        "callback": cl.debug
    },
    1: {
        "status": "success",
        "callback": cl.info
    },
    2: {
        "status": "warning",
        "callback": cl.warning
    },
    3: {
        "status": "error",
        "callback": cl.error
    },
    4: {
        "status": "error",
        "callback": cl.critical
    }
}

message = "Hello, simple log dynamique avec un niveau aléatoire"


# 1ère étape
total_iteration = 53
desc_1 = sb.init_sub_process(color="cyan")  # initialisation d'un sous process pour afficher les infos de la boucle
pb_1 = sb.init_progress_bar(total=total_iteration, desc="1ère Étape :")  # initialisation barre de progression

for i in range(total_iteration):
    _ = random.choice(range(5))
    log_methods[_]["callback"](f"{i} - {message}")  # ici les logs ne sont pas rattachées donc l'affichage se fait en défilement normal dans le terminal...
    duree_etape = sb.update_progress_bar(
        pb_1,
        status_text=f"{(i + 1): >{2}}/{total_iteration} - Reste {(total_iteration - i - 1): >{2}}".rjust(26, " "),
        status=log_methods[_]["status"]
    )
    sb.update_sub_process(desc_1, f"{i} - {message}")

cl.info(duree_etape)  # affichage durée 1ère étape


# 2ème étape
desc_2 = sb.init_sub_process(color="green", justify="left")  # initialisation sous process 2ème étape
sb.update_sub_process(desc_2, f"Coucou ceci est ma 2ème Étape et elle est affichée dans un sous process fils... "
                              "Après la fin du (ou des) process qui me précède(nt) =}")


# 3ème étape en erreur
factor = 2
pb_2 = sb.init_progress_bar(total=total_iteration * factor, color="magenta", desc="3ème Étape :")

for i in range(total_iteration * factor):
    duree_3_etape = sb.update_progress_bar(pb_2,
                                           f"{(i + 1): >{3}}/{total_iteration * factor}".rjust(23, " "),
                                           "error")

cl.info(duree_3_etape)  # affichage durée 3ème étape


# 4ème étape en warning
factor = 3
pb_3 = sb.init_progress_bar(total=total_iteration * factor, color="magenta", desc="4ème Étape :")

for i in range(total_iteration * factor):
    duree_4_etape = sb.update_progress_bar(pb_3,
                                           f"{(i + 1): >{3}}/{total_iteration * factor}".rjust(23, " "),
                                           "warning")

cl.info(duree_4_etape)  # affichage durée 4ème étape


# 5ème étape normale avec changement de couleur par défaut
factor = 4
pb_4 = sb.init_progress_bar(total=total_iteration * factor, color="magenta", desc="5ème Étape :")

for i in range(total_iteration * factor):
    duree_5_etape = sb.update_progress_bar(pb_4,
                                           f"{(i + 1): >{3}}/{total_iteration * factor}".rjust(23, " ")
                                           )

cl.info(duree_5_etape)  # affichage durée 4ème étape


duree_totale = sb.terminate()
cl.info(duree_totale)  # durée totale de statusbars.py
_images/status_bar.gif _images/status_bar.png
init_progress_bar(total=100, desc='Avancement :', status_text='', display_status=True, color='green')

Description

Permet d’initialiser une nouvelle barre de progression

Note

Il n’y a pas de limitations aux nombres de barres de progression. Les affichages et utilisations de ces dernières sont ordonnés selon l’ordre d’appel de la méthode décrite ici
Parameters
  • total – indique le total des itérations

  • desc – indique la description à afficher pour la barre de progression

  • status_text – indique et précise le texte de l’état si nécessaire

  • display_status – indique l’affichage de l’état Succes/Warning/Error avec leur volumétrie totale

  • color – indique la couleur de la barre de progression

Returns

un descripteur

Exemple :

# progress_bar.py
# %%

from corelibs import log, tools

# /!\ ATTENTION /!\
#     Jupyter Notebook n'étant pas un terminal, il ne se passera rien... =}
# Instanciations
sb = log.StatusBars()
cl = log.TermColorLog(output_2_log_file=False)


# %%
# Progress bar sur un fichier
fichier = r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8_head_preview.csv"

total_iteration = tools.get_total_lines_in_file(fichier) - 1  # dernière ligne à blanc comme dans tous fichiers plats
pb_1 = sb.init_progress_bar(total=total_iteration - 1, color="magenta")  # initialisation barre de progression

with open(fichier) as f_in:
    for i, f in enumerate(f_in):
        cl.info(f"N° : {i} - Valeur lue : {f}")  # ou process quelconque...
        duree_etape = sb.update_progress_bar(
            pb_1,
            status_text=f"{i + 1: >{3}}/{total_iteration - 1} - Reste {(total_iteration - i - 2): >{3}}"
        )

duree_totale = sb.terminate()
cl.info(duree_totale)
init_sub_process(title=None, color='white', fill=' ', justify='center')

Description

Permet d’initialiser une nouvelle ligne de barre d’état pour gérer un processus enfant. Chaque processus enfant peut hériter d’une ou plusieurs barre d’état en utilisatant son descripteur

Note

Il n’y a pas de limitations aux nombres de sous-barres d’état. Les affichages et utilisations de ces dernières sont ordonnés selon l’ordre d’appel de la méthode décrite ici
Parameters
  • title – indique le titre de la nouvelle sous barre d’état

  • color – indique la couleur souhaitée pour le titre

  • fill – indique le caractère de remplissage

  • justify

    indique la justification du texte

    valeur possibles: left, center ou right

Returns

un descripteur

terminate()

Description

Permet de terminer l’instance StatusBars et renvoie le temps total d’exécution
Returns

tuple nommé avec comme attributs :
  • duration

  • duration_in_second

update_progress_bar(progress_bar_descriptor, status_text='', status='success', increment=1)

Description

Permet de mettre à jour la barre de progression
Parameters
  • progress_bar_descriptor – indique le descripteur retourné par la méthode corelibs.log.StatusBars.init_progress_bar()

  • status_text – indique et précise le texte de l’état si nécessaire

  • status

    indique et précise l’état d’avancement

    valeur possibles: success, warning, error
    valeur par défaut: success

  • increment – indique la valeur incrémentale

Returns

tuple nommé avec comme attributs :
  • duration

  • duration_in_second

update_sub_process(sub_process_descriptor, phases)

Description

Permet de mettre à jour la sous-barre d’état
Parameters
Returns

rien…

class corelibs.log.StopWatch(log_handler=None, display_status_bar=False)

Description

Classe permettant de chronométrer un programme ou une portion de programme

Le niveau minimal pour afficher les informations doit être de log.INFO (cf. Installation & Mise à jour pour plus d’informations config.DEFAULT_LOG_LEVEL)

Parameters
  • log_handler – indique si le timing doit faire une sortie dans un fichier log, défini via l’instanciation de cf. ColorLog ou cf. TermColorLog.

  • display_status_bar

    indique s’il faut ou non afficher la barre de statut dans la sortie standard

    valeur possibles: False/True
    valeur par défaut: False

Returns

rien…

Exemple :

# stopwatch.py
from corelibs import log


# Instanciation log
cl = log.TermColorLog(output_2_log_file=False)

# Instanciation Chronomètre
sw = log.StopWatch(cl)


# Ajout décorateur timing() pour calculer le temps d'exécution de la fonction `ma_fonction()`
@log.timing()
# Cumuler décorateur status_bar() pour afficher la barre de statut
@log.status_bar()
def ma_fonction(nb):
    for _ in range(nb):
        pass


ma_fonction(200000000)
ma_fonction(200000000)

duration = sw.stop()  # ici nous avons les infos sur la durée totale d'exécution...
cl.info(duration)
_images/stopwatch.png
stop()

Description

méthode pour arrêter le chronomètre
Returns

tuple nommé avec comme attributs :
  • duration

  • duration_in_second

class corelibs.log.TermColorLog(name=None, log_level=20, output_2_log_file=True, location=None, log_file_name=None, field_styles=None, level_styles=None, log_format=None, log_date_format=None)

Description

Classe de base pour manipuler les logs colorées dans la sortie standard du terminal.

cf. Installation & Mise à jour pour la configuration par défaut (modifiable lors de l’instanciation de la classe)

Parameters
  • name

    indique le nom de la log en cours

    valeur possibles: None/nom de la log
    valeur par défaut: None

  • log_level

    indique le niveau minimum d’alerte pour l’affichage des logs

    valeur possibles: None/niveau d’alerte
    valeur par défaut: config.DEFAULT_LOG_LEVEL

  • output_2_log_file

    indique s’il faut ou non écrire les logs dans un fichier en sortie

    valeur possibles: False/True
    valeur par défaut: True

  • location

    indique l’emplacement des fichiers logs de sortie

    valeur possibles: None/chemin dossier logs
    valeur par défaut: None
    • Si location non renseigné, par défaut, les logs seront enregistrés dans le dossier retourné par

      corelibs.lazy.mkdir()

    • Sinon les logs seront enregistrés dans le dossier location\DEFAULT_DIR_SCAFFOLDING[“logs”][“name”]

  • log_file_name

    indique le nom de la log

    valeur possibles: None/nom de la log
    valeur par défaut: None

  • field_styles

    permet de définir le style d’affichage des logs dans la sortie standard

    valeur possibles: None/dictionnaire
    valeur par défaut: None

  • level_styles

    permet de définir le style d’affichage des niveaux d’alerte des logs dans la sortie standard

    valeur possibles: None/dictionnaire
    valeur par défaut: None

  • log_format

    permet de définir le format d’affichage de la log

    valeur possibles: None/format d’affichage de la log
    valeur par défaut: None

  • log_date_format

    permet de définir le format d’affichage horodaté de la log

    valeur possibles: None/format d’affichage horodaté de la log
    valeur par défaut: None

Note

cf. Installation & Mise à jour :
  • DEFAULT_LOG_LEVEL

  • DEFAULT_FIELD_STYLES

  • DEFAULT_LEVEL_STYLES

  • DEFAULT_DIR_SCAFFOLDING

  • DEFAULT_LOGS_EXTENSION

  • DEFAULT_LOG_FORMAT

  • DEFAULT_LOG_DATE_FORMAT

Exemple :

# termcolorlog.py
from corelibs import config, log

# écrasement de la config par défaut de corelibs
# config.DEFAULT_VERBOSE = True
config.DEFAULT_LOGS_EXTENSION = ".LOG"
# config.DEFAULT_SHORT_LOG_LABEL = False
# config.DEFAULT_STACK_TRACE_2_FILE = True
# config.DEFAULT_STYLE_STACK_TRACE = "plaintext"
# config.DEFAULT_STACK_TRACE = True
# config.DEFAULT_CONTEXT_SOURCE_LINES = 7

# instanciation par défaut pour la sortie standard terminal seulement
cl = log.TermColorLog()

cl.debug("Bonjour, ceci est un test niveau DEBUG")
cl.info("Bonjour, ceci est un test niveau INFO", True)
cl.warning("Bonjour, ceci est un test niveau WARNING")
cl.error("Bonjour, ceci est un test niveau ERROR", trace_back=True)
cl.critical("Bonjour, ceci est un test niveau CRITIQUE")


# redéfinition du nom du dossier logs
# /!\ IMPORTANT /!\
#    il n'y a que le dossier des logs qui est pris en compte, définir les autres dossiers ici est inutile
config.DEFAULT_DIR_SCAFFOLDING = {
    "logs": {  # dossier contenant toutes les sorties "logs"
        "name": "__R2D2-LOGS__",
        "make": True
    },
}

# instanciation personnalisée
user_cl = log.TermColorLog(
    name="Ma log à moi que j'ai",
    log_level=None,
    output_2_log_file=True,
    location=r"D:\OneDrive\Documents\_TEST_\T32020",
    log_file_name=None,
    field_styles={
        "asctime": {"color": "black"},
        "name": {"color": "green"},
    },
    level_styles={
        "info": {
            "color": "white",
            "background": "cyan"
        },
    },
    log_format="%(asctime)s - %(name)s [%(filename)s:%(lineno)07d] <> %(levelname)13s : %(message)s",
    log_date_format="%A %d %B %Y"
)

user_cl.debug("Bonjour, ceci est un test personnalisé niveau DEBUG")
user_cl.info("Bonjour, ceci est un test personnalisé niveau INFO")
user_cl.warning("Bonjour, ceci est un test personnalisé niveau WARNING")
user_cl.error("Bonjour, ceci est un test personnalisé niveau ERROR", trace_back=True)
user_cl.critical("Bonjour, ceci est un test personnalisé niveau CRITIQUE", trace_back=True)


# Création log dynamiquement selon contexte terminal standard ou Jupyter Notebooks
xcl = log.ColorLog(log_file_name="KIM")
xcl.debug("Bonjour, ceci est un test dynamique niveau DEBUG")
xcl.info("Bonjour, ceci est un test dynamique niveau INFO")
xcl.warning("Bonjour, ceci est un test dynamique niveau WARNING")
xcl.error("Bonjour, ceci est un test dynamique niveau ERROR", trace_back=True)
xcl.critical("Bonjour, ceci est un test dynamique niveau CRITIQUE", trace_back=True)
corelibs.log.args_dumping(wrapped_func)

Description

Décorateur pour lister tous les arguments passés dans une fonction décorée

Le niveau minimal pour afficher les informations doit être de log.DEBUG (cf. Installation & Mise à jour pour plus d’informations config.DEFAULT_LOG_LEVEL)

Exemple :

# args_dumping.py
from corelibs import config, log
from random import randrange

my_dict = {
    0: {
        "nom": "MARIE ADÉLIE",
        "prénom": "Kim",
        "age": 7
    },
    1: {
        "arg1": "Hello",
        "arg2": "Kim"
    },
    2: {
        "msg": "I ❤  U",
        "from": "papa"
    }
}


# le niveau d'alerte par défaut est à INFO, correspondant à la valeur 20...
# 10 correspond à DEBUG, ce qui a pour effet d'afficher le dumping...
config.DEFAULT_LOG_LEVEL = 10  # décommenter pour baisser le niveau d'alerte à DEBUG


# décoration pour lister dynamiquement les arguments passés en paramétrage de la fonction `test_dumping(...)
@log.args_dumping
def test_dumping(iteration, *args, **kwargs):
    pass


total_dict = len(my_dict)
for i in range(7):  # appel dynamique aléatoire d'arguments dans la fonction `test_dumping()`
    rand_number = randrange(total_dict)
    test_dumping(
        i,
        rand_number,
        "itération {i} et nb aléatoire {rand_number}".format(i="{:0>3}".format(i), rand_number=rand_number),
        sub_dict=my_dict[rand_number]
    )
_images/decorator_args_dumping.png
corelibs.log.dict_dumping(wrapped_func)

Description

Décorateur pour afficher toutes les fonctions qui retournent un dictionnaire dans un but de vérification.

Le niveau minimal pour afficher les informations doit être de log.DEBUG (cf. Installation & Mise à jour pour plus d’informations config.DEFAULT_LOG_LEVEL)

Exemple :

# dict_dumping.py
from corelibs import config, log, tools as to

# le niveau d'alerte par défaut est à INFO, correspondant à la valeur 20...
# 10 correspond à DEBUG, ce qui a pour effet d'afficher le dumping...
config.DEFAULT_LOG_LEVEL = 10  # décommenter pour baisser le niveau d'alerte à DEBUG


@log.dict_dumping
def test_namedtuple():
    filename = r"D:\OneDrive\Documents\_TEST_\2020-11-11.jpg"
    file_properties = to.get_file_properties(filename, pretty_byte_size=False)
    return file_properties


# affichage simple du tuple nommé pas toujours évident à lire selon complexcité
print(test_namedtuple())  # FileProperties(st_mode=33206, st_ino=11540474045256220, st_dev=3199390331, st_nlink=1, st_uid='Invités', st_gid=0, st_size=ByteSize(byte=98569, kilobyte=96.26, megabyte=0.09, gigabyte=0.0, terabyte=0.0), st_atime='14/11/2020 22:27:01', st_mtime='11/11/2020 22:10:46', st_ctime='11/11/2020 22:10:39')
test_namedtuple()
_images/dict_dumping.png
corelibs.log.progress_bar(title=None, desc=None, desc_padding=32, color='green', terminate=False)

Description

Décorateur pour afficher l’avancement des différentes étapes process sous forme de barre de progression
Parameters
  • title – indique le titre de la barre

  • desc – indique la description à afficher pour la barre de progression

  • desc_padding – indique la taille de rembourrage

  • color – indique la couleur de la barre de progression

  • terminate – indique au décorateur de clore les descripteurs et de libérer le terminal pour un affichage normal

Returns

rien…

Exemple :

# progress_bar_decorator_version.py
import time

from corelibs import log

cl = log.ColorLog(output_2_log_file=False)


# Ajout décorateur status_bar() pour afficher des informations dans le terminal
# /!\ ATTENTION /!\
#     Jupyter Notebook n'étant pas un terminal, il ne se passera rien... =}
@log.progress_bar(title="Ma barre d'état décorée", desc="Ma première étape", color="magenta", desc_padding=64)
def ma_fonction_avec_barre_statut(nb):
    time.sleep(3)


ma_fonction_avec_barre_statut(3)
cl.info("Durée 1ère étape " + str(log.decorator_return["duration"]["progress_bar"]))
print("\n")


@log.progress_bar(color="cyan", desc_padding=64)  # Sans titre...
def ma_deuxieme_fonction_de_la_morkitu_avec_decorateur():
    for _ in range(3):
        cl.info(f"{_: >3} : les informations défilent au dessus de la 2ème barre...")
    time.sleep(5)


ma_deuxieme_fonction_de_la_morkitu_avec_decorateur()
print("\n")
cl.info("Durée 2ème étape " + str(log.decorator_return["duration"]["progress_bar"]))


@log.progress_bar(terminate=True, color="white", desc_padding=64)  # Ne pas oublier le terminate pour relâcher la barre d'état!!!
def ma_derniere_fonction_avec_terminate():
    time.sleep(7)


ma_derniere_fonction_avec_terminate()
cl.info("Durée dernière étape " + str(log.decorator_return["duration"]["progress_bar"]))
cl.info("Durée Totale : " + log.decorator_return["duration"]["total"].duration)


# Arrêt et fonctionnement normal à partir de maintenant...
print("\n")
for _ in range(3):
    print("hé "*((_+1)*2) + "... =}")
_images/progress_bar.gif _images/progress_bar.png
corelibs.log.stack_trace(force=False)

Description

Décorateur pour afficher le détail des piles d’exécution (dans le cadre d’un débug).
Parameters

force

permet de forcer localement l’affichage détaillé

cf. Installation & Mise à jour :
  • DEFAULT_STACK_TRACE

  • DEFAULT_CONTEXT_SOURCE_LINES

Returns

rien…

Exemple :

# stack_trace.py
from corelibs import log, config

# config.DEFAULT_STACK_TRACE = True  # on force globalement l'affichage détaillé des piles d'exécution


def palindrome(mot):
    mots = list(mot)
    len_mot = len(mots)
    for i in range(int(len_mot / 2) + 1):
        if mot[i].lower() != mot[(len_mot - 1) - i].lower():
            return False

    return True


@log.stack_trace(force=True)  # on force localement l'affichage détaillé des piles d'exécution
def is_palindrome(mot):
    if palindrome(mot):
        print("\"{mot}\" est un palindrome".format(mot=mot))
    else:
        print("\"{mot}\" n'est pas un palindrome".format(mot=mot))


is_palindrome("Hello Kim, c'est papa =}")  # False
is_palindrome("saippuakauppias")  # True
_images/stack_trace.png
corelibs.log.status_bar(title=None)

Description

Décorateur pour figer sur le terminal, tout en bas, l’état d’avancement.

La barre de statut n’existe que pendant le temps de l’exécution de la fonction décorée (i.e. lors de l’appel d’une autre fonction, cette barre disparait si cette dernière fonction n’est elle-même pas décorée).

Parameters

title – permet de donner un label à la barre de statut

Returns

rien…

Exemple :

# statusbars_decorator.py
from corelibs import log


# Ajout décorateur status_bar() pour afficher des informations dans le terminal
# /!\ ATTENTION /!\
#     Jupyter Notebook n'étant pas un terminal, il ne se passera rien... =}
@log.status_bar("Ma barre de statut figée")
def ma_fonction_avec_barre_statut(nb):
    for _ in range(nb):
        print("{_} : les informations défilent au dessus de la 1ère barre...".format(_="{:0>3}".format(_)))


ma_fonction_avec_barre_statut(3)
print(log.decorator_return["duration"]["status_bar"])  # durée de l'étape status_bar
print("\n")


@log.status_bar()  # Sans titre...
def ma_deuxieme_fonction_de_la_morkitu_avec_decorateur():
    for _ in range(3):
        print(f"{_: >3} : les informations défilent au dessus de la 2ème barre...")


ma_deuxieme_fonction_de_la_morkitu_avec_decorateur()
print(log.decorator_return["duration"]["status_bar"])  # durée de la seconde étape (car écrasement)


# Arrêt et fonctionnement normal à partir de maintenant...
print("\n")
for _ in range(3):
    print("hé "*((_+1)*2) + "... =}")
corelibs.log.timing(log_handler=None)

Description

Décorateur pour chronométrer le temps d’exécution de toutes fonctions cibles
Parameters

log_handler – indique si le timing doit faire une sortie dans un fichier log, défini via l’instanciation de cf. ColorLog ou cf. TermColorLog.

Returns

rien…

Exemple :

# timing.py
from corelibs import log
from numba import njit


cl = log.ColorLog()  # 1. créer une instance de ColorLog()


# Ajout décorateur timing() pour calculer le temps d'exécution de la fonction `ma_premiere_fonction_optimisee()`
@log.timing()
@njit()
def ma_premiere_fonction_optimisee(nb):
    total = 0
    for _ in range(nb):
        total = total + 1

    return total


cl.info(
    "le total est "
    + "{0:,}".format(
        ma_premiere_fonction_optimisee(30000000000)
    ).replace(",", " ")
)


# Faire sortir le résultat du décorateur timing() dans un fichier log spécifique, ici, l'objet `cl`
@log.timing(cl)
def ma_deuxieme_fonction_non_optimisee(nb):  # définition d'une 2ème fonction...
    total = 0
    for _ in range(nb):
        total = total + 1

    return total


cl.info(
    "le total est "
    + "{0:,}".format(
        ma_deuxieme_fonction_non_optimisee(30000000000)
    ).replace(",", " ")
)
_images/decorator_timing.png

Module lazy

Description générale

Module de base avec des fonctions, décorateurs utiles, etc…

corelibs.lazy.add_dir_path_2_project(path)

Description

Permet d’inclure dans le projet actuel des programmes python tiers, enregistrés dans un emplacement différent.

Warning

En principe, il est très rare de faire appel à cette fonction. Si cela se répète, il faudrait éventuellement revoir la structure du projet.
Parameters

path – indique l’emplacement en chemin absolu du dossier contenant le ou les programmes python à inclure dans le projet actuel.

Returns

rien…

Exemple :

# D:\OneDrive\Documents\_TEST_\PY_2_IMPORT\programme_importe.py


# Les instructions ci-dessous n'appartiennent pas au projet... et son emplacement source est D:\OneDrive\Documents\_TEST_\PY_2_IMPORT
def say_hello(who):
	print("Hello", who)
# add_dir_path_2_project.py
from corelibs import lazy as lz

# inclusion du dossier parent contenant les programmes à inclure, ici D:\OneDrive\Documents\_TEST_\PY_2_IMPORT
lz.add_dir_path_2_project(r"D:\OneDrive\Documents\_TEST_\PY_2_IMPORT")

try:
    import programme_importe as pi
except ImportError:
    raise Exception("\n\nProblème import programme tiers d'un emplacement loufoque, hors projet")

pi.say_hello("Kim!")  # affichera Hello Kim! si tout est OK
corelibs.lazy.convert_2_dict(obj)

Description

Permet de convertir tous les objets (convertissables) en dictionnaire de manière récursive.
Parameters

obj – objet à convertir

Returns

dictionnaire
ou
objet contenant un dictionnaire imbriqué converti

Exemple :

# convert_2_dict.py
# %%
from collections import namedtuple
from corelibs import lazy as lz


# création d'un objet PuPuce tuple nommé
PuPuce = namedtuple("PuPuce", ["name", "age"])
kim = PuPuce(
    name="Kim",
    age=6
)
print(kim)  # affiche PuPuce(name='Kim', age=6)
print(lz.is_namedtuple_instance(kim))  # affiche bien True

# %%
# conversion en dictionnaire
print(lz.convert_2_dict(kim))  # affiche {'name': 'Kim', 'age': 6}

# %%
# création d'un tuple nommé imbriqué
NestedPuPuce = namedtuple("NestedPuPuce", ["name", "age", "nested_tuple"])
kim2 = NestedPuPuce(
    name="Kim",
    age=6,
    nested_tuple=kim
)
print(kim2)  # affiche NestedPuPuce(name='Kim', age=6, nested_tuple=PuPuce(name='Kim', age=6))

# %%
# conversion en dictionnaire
print(lz.convert_2_dict(kim2))  # {'name': 'Kim', 'age': 6, 'nested_tuple': {'name': 'Kim', 'age': 6}}

# %%
# tableau non convertissable
print(lz.convert_2_dict(["TRUONG", "Kim", 6]))  # ['TRUONG', 'Kim', 6]

# %%
# conversion d'un tuple nommé au sein du tableau...
print(lz.convert_2_dict(["TRUONG", "Kim", 6, kim2]))  # ['TRUONG', 'Kim', 6, {'name': 'Kim', 'age': 6, 'nested_tuple': {'name': 'Kim', 'age': 6}}]
corelibs.lazy.copy(source, destination)

Description

Permet de copier un ou des fichiers vers une nouvelle destination ou des nouvelles destinations. Les fichiers sont au sens Unix du terme (i.e. soit fichier régulier, soit répertoire)

Warning

La copie lèvera une alerte si des sous répertoires destinations existent et portent les mêmes noms que les sous répertoire sources.
Parameters
  • source – indique l’emplacement source du ou des fichiers à copier avec le(s) chemin(s) absolu(s), avec ou sans schéma.

  • destination – indique l’emplacement destination du ou des fichiers à copier avec le(s) chemin(s) absolu(s).

Returns

rien…

Exemple :

# copy.py
# %%
from corelibs import lazy as lz


# %%
# Création dossier destination
repertoire_destination = r"D:\OneDrive\Documents\_TEST_\_COPY_DESTINATION_"
lz.mkdir(repertoire_destination, make_scaffolding=False)

# %%
# Copie simple de fichier standard, sans renommage
lz.copy(r"\\wsl$\Ubuntu-20.04\root\.zsh_history", repertoire_destination)  # chemin réseau...

# %%
# Copie simple de fichier standard, avec renommage
lz.copy(r"D:\OneDrive\Documents\_TEST_\_éèçàoöôîïêëùûü;.txt", repertoire_destination + "\\nouveau_nom.txt")

# %%
# Copie simple de répertoire standard, sans renommage
lz.copy(r"D:\OneDrive\Documents\_TEST_\__R2D2-LOGS__", repertoire_destination + "\\__R2D2-LOGS__")  # IMPORTANT!!! remettre le même nom de dossier destination, autrement, la copie se fera au niveau du répertoire parent

# %%
# Copie simple de répertoire standard, avec renommage
lz.copy(r"D:\OneDrive\Documents\_TEST_\__R2D2-LOGS__", repertoire_destination + "\\__R2D2__")

# %%
# Copie via mode modèle
lz.copy(r"D:\OneDrive\Documents\_TEST_\*.sas*", repertoire_destination)  # modèle avec extension
# ou
lz.copy(r"D:\OneDrive\Documents\_TEST_\*2020-11-11*", repertoire_destination)  # modèle sans extension, comprenant la chaîne "2020-11-11" dans le nom

# %%
# Copie groupée dans un dossier
lz.copy((
    r"D:\OneDrive\Documents\_TEST_\*.sas*",  # avec schéma
    r"D:\OneDrive\Documents\_TEST_\2020-11-11.jpg",
    r"D:\OneDrive\Documents\_TEST_\__R2D2-LOGS__"  # dossier...
), repertoire_destination)

# %%
# Copie groupée d'une liste de fichiers dans une autre liste de fichiers (fonctionnement 1-1, i.e. même nombre de fichiers en entrée et en sortie, traitée de manière itérative)
lz.copy((
    r"D:\OneDrive\Documents\_TEST_\*.sas*",  # avec schéma
    r"D:\OneDrive\Documents\_TEST_\2020-11-11.jpg",
    r"D:\OneDrive\Documents\_TEST_\__R2D2-LOGS__",  # dossier...
), (
    repertoire_destination,  # sans renommage
    repertoire_destination + "\\2020-11-11_NOUVEAU_NOM.jpg",  # avec renommage
    repertoire_destination + "\\__R2D2-NEW__",  # avec renommage
))
corelibs.lazy.datetime_2_epoch(date_time, time_format='%d/%m/%Y %H:%M:%S', reference_epoch='Unix', ignore_errors=False)

Description

Permet de convertir une date/heure en nombre de secondes écoulés depuis le temps de référence epoch.
cf. corelibs.lazy.epoch_2_datetime() pour la conversion inverse
Parameters
  • date_time – date à convertir

  • time_format

    indique le format de la date et heure

    valeur par défaut: “%d/%m/%Y %H:%M:%S” (DD/MM/AAAA HH:MM:SS)

  • reference_epoch

    indique l’époque de référence

    valeurs possibles: “Unix”, “Windows”
    valeur par défaut: “Unix”

  • ignore_errors

    forcer l’exécution lorsqu’une erreur est levée

    valeurs possibles: False/True
    valeur par défaut: False (cf. DEFAULT_IGNORE_ERROR dans Installation & Mise à jour)

Returns

timestamp au format epoch

Exemple :

# datetime_2_epoch.py
# %%
from corelibs import lazy as lz

# conversion en epoch Unix (https://www.epochconverter.com/#tools)
print(lz.datetime_2_epoch(date_time=" Oct 3 1978       1:33PM  ", time_format="%b %d %Y %I:%M%p"))  # même si ce type de chaîne loufoque est nettoyée avant le calcul, il est préférable d'écrire proprement la donnée...
# 276269580000
print(lz.datetime_2_epoch("28/11/2013 19:23:52"))
# 1385666632000

# conversion en epoch Windows (https://www.epochconverter.com/ldap)
print(lz.datetime_2_epoch(date_time="Oct 3 1978 1:33PM", time_format="%b %d %Y %I:%M%p", reference_epoch="Windows"))
# 119207431800000000
print(lz.datetime_2_epoch(date_time="28/11/2013 19:23:52", reference_epoch="Windows"))
# 130301402320000000
corelibs.lazy.delete_files(file_path, extension='', remove_empty_dir=True, verbose=False)

Description

Permet de supprimer des fichiers ou répertoires récursivement. La suppression peut se faire également en se basant sur plusieurs extensions différentes (cf. exemple)
Parameters
  • file_path – le chemin absolu du fichier ou du répertoire à supprimer

  • extension

    le(s) extension(s) ou modèle des fichiers à supprimer

    valeurs possibles: expression régulière pour désigner des extensions/modèles de nom de fichiers
    valeur par défaut: rien (pour éviter les erreurs…)

  • remove_empty_dir

    indique si il faut supprimer ou non le répertoire vidé

    valeurs possibles: False/True
    valeur par défaut: True

  • verbose

    afficher ou non les messages d’info/alertes

    valeurs possibles: False/True
    valeur par défaut: DEFAULT_VERBOSE (cf. Installation & Mise à jour)

Returns

rien…

Exemple :

# delete_files.py
# %%
from corelibs import lazy as lz


# %%
# Suppression d'un fichier ciblé et nommé "Nouveau document texte.txt"
file_2_del = r"D:\OneDrive\Documents\_TEST_\DosASupp\Nouveau document texte.txt"
lz.delete_files(file_2_del, verbose=True)

# %%
# Suppression de tous les fichiers avec une extension .RTF
files_2_del = r"D:\OneDrive\Documents\_TEST_\DosASupp"
lz.delete_files(files_2_del, extension="*.rtf", verbose=True)

# %%
# Suppression de tous les fichiers avec une extension .DOC* et .XLS*
files_2_del = r"D:\OneDrive\Documents\_TEST_\DosASupp"
lz.delete_files(files_2_del, extension="*.doc*,*.xls*", verbose=True)

# %%
# L'extension peut prendre un modèle regex, par exemple :
# Suppression de tous les fichiers ayant le mot _LOG_ dans le nom
files_2_del = r"D:\OneDrive\Documents\_TEST_\DosASupp"
lz.delete_files(files_2_del, extension="*_LOG_*", verbose=True)

# %%
# Suppression complète
#   • tous les fichiers à l'intérieur du dossier DosASupp
#   • une fois vide, le dossier DosASupp est supprimé
folder_2_del = r"D:\OneDrive\Documents\_TEST_\DosASupp"
lz.delete_files(folder_2_del, extension="*", verbose=True)
corelibs.lazy.epoch_2_datetime(seconds, time_format='%d/%m/%Y %H:%M:%S')

Description

Permet de convertir un nombre de secondes vers un format date/heure spécifié. L’epoch est le temps initial de référence, à partir duquel on mesure les secondes écoulées pour calculer les dates/heures. Sous UNIX/POSIX, ce temps correspond au 1 janvier 1970 00:00:00 UT et sous Windows NT, 1 janvier 1601 00:00:00 UT.
cf. corelibs.lazy.datetime_2_epoch() pour la conversion inverse
Parameters
  • seconds – indique le nombre de secondes

  • time_format

    indique le format de sortie souhaité

    valeur par défaut: “%d/%m/%Y %H:%M:%S” (DD/MM/AAAA HH:MM:SS)

Returns

timestamp
ou
None (si problème)

Exemple :

# epoch_2_datetime.py
from corelibs import lazy as lz

print(lz.epoch_2_datetime(1605050212))
# 11/11/2020 00:16:52
corelibs.lazy.get_abspath(root_dir_path, dir_2_join)

Description

Retourne le chemin absolu normalisé à partir d’un couple (chemin, dossier)
Parameters
  • root_dir_path – le chemin absolu

  • dir_2_join – le dosssier à concaténer au chemin absolu

Returns

le chemin normalisé

Exemple :

# get_abspath.py
from corelibs import lazy as lz

abs_path = lz.get_abspath(r"C:\documents/dir", "toto")
print(f"Le chemin absolu normalisé est \"{abs_path}\"")

Terminal :

$ python get_abspath.py
Le chemin absolu normalisé est "C:\documents\dir\toto"
corelibs.lazy.get_bytes_size_4_human(byte_size_format, default_format=None, min_byte_size_format={'Go': {'min_size': 1}, 'Ko': {'min_size': 1}, 'Mo': {'min_size': 1}, 'To': {'min_size': 0.5}, 'octet': {'min_size': 0}}, size_unit=True, ignore_errors=False)

Description

Permet de lister un tuple nommé contenant toutes les valeurs converties à partir d’une taille en octet.
Parameters
  • byte_size_format – tuple nommé calculé par corelibs.lazy.get_bytes_size_formats()

  • default_format – format d’affichage souhaité | valeur par défaut: None, laissant le choix à la fonction de retourner la meilleure valeur | valeurs possible: “octet”, “Ko”, “Mo”, “Go” ou “To”

  • min_byte_size_format – lorsque le format d’affichage default_format est à None alors sera calculé automatiquement le meilleur format à afficher, dont les seuils minimums sont définis dans DEFAULT_MIN_BYTE_SIZE_FORMAT (cf. Installation & Mise à jour)

  • size_unit

    afficher l’unité de mesure

    valeurs possibles: False/True
    valeur par défaut: True

  • ignore_errors

    forcer l’exécution lorsqu’une erreur est levée

    valeurs possibles: False/True
    valeur par défaut: False (cf. DEFAULT_IGNORE_ERROR dans Installation & Mise à jour)

Returns

string sous la forme XX.XX Unité (où Unité est octet, Ko, Mo, Go ou To)
ou
float (si l’unité de mesure n’est pas souhaitée)

Exemple :

# get_bytes_size_4_human.py
# %%
from corelibs import lazy as lz


# %%
byte_size = lz.get_bytes_size_formats(0)
print(lz.get_bytes_size_4_human(byte_size))
# affiche par défaut la valeur 0 octet

# %%
byte_size = lz.get_bytes_size_formats(153800565)
print(lz.get_bytes_size_4_human(byte_size))
# affiche par défaut la valeur 146.68 Mo

# %%
byte_size = lz.get_bytes_size_formats(1739886085)
print(lz.get_bytes_size_4_human(byte_size))
# affiche par défaut la valeur 1.62 Go

# %%
# affichage forcé dans une unité de mesure souhaitée
print(lz.get_bytes_size_4_human(byte_size, default_format="Mo"))  # affiche 1 659.28 Mo

# %%
# affichage forcé en Mo, mais sans l'unité de mesure
print(lz.get_bytes_size_4_human(byte_size, size_unit=False, default_format="Mo"))  # affiche 1659.28
corelibs.lazy.get_bytes_size_formats(byte_size)

Description

Permet de lister un tuple nommé contenant toutes les valeurs converties à partir d’une taille en octet.
Parameters

byte_size – indique le nombre d’octets

Returns

tuple nommé avec comme attributs :
  • byte

  • kilobyte

  • megabyte

  • gigabyte

  • terabyte

Exemple :

# get_bytes_size_formats.py
# %%
from corelibs import lazy as lz


print(lz.get_bytes_size_formats(0))
# affiche ByteSize(byte=0, kilobyte=0.0, megabyte=0.0, gigabyte=0.0, terabyte=0.0)
print(lz.get_bytes_size_formats(153800565))
# affiche ByteSize(byte=153800565, kilobyte=150195.86, megabyte=146.68, gigabyte=0.14, terabyte=0.0)
print(lz.get_bytes_size_formats(1739886085))
# affiche ByteSize(byte=1739886085, kilobyte=1699107.5, megabyte=1659.28, gigabyte=1.62, terabyte=0.0)

corelibs.lazy.get_caller_line_number(back_level=3, ignore_errors=False)

Description

Permet de connaître la ligne du module source appelant un autre module.
Parameters
  • back_level

    profondeur d’appel de la fonction parent/enfant

    valeurs possibles: entier
    valeur par défaut: 3

  • ignore_errors

    forcer l’exécution lorsqu’une erreur est levée

    valeurs possibles: False/True
    valeur par défaut: False (cf. DEFAULT_IGNORE_ERROR dans Installation & Mise à jour)

Returns

ligne du module appelant

Exemple :

# get_caller_line_number.py
# %%
from corelibs import config, lazy as lz


# appel croisé correct...
def info(profondeur_appel=1):
    return lz.get_caller_line_number(profondeur_appel)


print(f"Le numéro de la ligne du module appelant est {info(profondeur_appel=1)}")

print(f"Le numéro de la ligne du module appelant est {info(profondeur_appel=2)}")

# appel direct incorrect...
lz.get_caller_line_number()
corelibs.lazy.get_caller_module_name()

Description

Permet de connaître le nom du module source appelant un autre module.
Returns

nom du module appelant

Exemple :

# get_caller_module_name.py
from corelibs import lazy as lz


def get_module_info():
    caller_name = lz.get_caller_module_name()
    print(f"Le nom du module python appelant est \"{caller_name}\"")
# caller_module_name.py
from tests.lazy import get_caller_module_name as gcmn


gcmn.get_module_info()

Terminal :

$ python caller_module_name.py
Le nom du module python appelant est "caller_module_name.py"
corelibs.lazy.get_closest_value_in_list(value, list_of_values, ignore_errors=False)

Description

Permet de récupérer la valeur la plus proche (en delta absolu) dans une liste de valeurs pour une valeur donnée.
Parameters
  • value – indique la valeur de référence

  • list_of_values – liste de valeurs (tuples ou tableaux)

  • ignore_errors

    forcer l’exécution lorsqu’une erreur est levée

    valeurs possibles: False/True
    valeur par défaut: False (cf. DEFAULT_IGNORE_ERROR dans Installation & Mise à jour)

Returns

valeur la plus proche trouvée

Exemple :

# get_closest_value_in_list.py
from corelibs import lazy as lz

print(
    lz.get_closest_value_in_list(1.6, [1, 2, 3])
)  # retourne 2 qui est la valeur la plus proche de 1.6 en delta absolu

print(
    lz.get_closest_value_in_list(74.7, (10, 20, 30, 40, 50, 60, 70, 80, 90, 100))
)  # retourne 70


array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
number = 103
print(lz.get_closest_value_in_list(number, array))  # retourne 122
corelibs.lazy.get_dir_n_basename(path)

Description

Retourne un tuple (chemin, nom fichier ou nom répertoire) à partir d’un chemin absolu normalisé (e.g. “C:\documents\dir” retournera “dir” et “C:\documents\dir\fichier.txt” retournera “fichier.txt”)
Parameters

path – chemin absolu normalisé

Returns

tuple nommé avec comme attributs :
  • dir_path

  • base_name

Exemple :

# get_dir_n_basename.py
# %%
from corelibs import lazy as lz


# %%
# chemin + fichier (si chemin seul, sera retourné alors le nom du dossier + le chemin amenant au dossier)
dir_n_basename = lz.get_dir_n_basename(r"C:\Users\M47624\corelibs\tests\lazy\get_dir_n_basename.py")
# récupération par index
print("Le chemin est \"{dir}\"".format(dir=dir_n_basename[0]))
print("Le fichier est \"{base}\"".format(base=dir_n_basename[1]))

# %%
# récupération par attributs
print("Le chemin est \"{dir}\"".format(dir=dir_n_basename.dir_path))
print("Le fichier est \"{base}\"".format(base=dir_n_basename.base_name))

# %%
# sortie "séparée" ou "déballée" (unpacked)
dir, base = lz.get_dir_n_basename(r"C:\Users\M47624\corelibs\tests\lazy\get_dir_n_basename.py")
print("Le chemin est \"{dir}\"".format(dir=dir))
print("Le fichier est \"{base}\"".format(base=base))

Terminal :

$ python get_dir_n_basename.py
Le chemin est "C:\Users\M47624\corelibs\tests\lazy"
Le fichier est "get_dir_n_basename.py"
Le chemin est "C:\Users\M47624\corelibs\tests\lazy"
Le fichier est "get_dir_n_basename.py"
Le chemin est "C:\Users\M47624\corelibs\tests\lazy"
Le fichier est "get_dir_n_basename.py"
corelibs.lazy.get_file_extension(filename, extensions=True, split_extensions=False)

Description

Retourne un tuple (nom fichier, .extension(s))
Parameters
  • filename – nom du fichier avec extension (avec ou sans chemin)

  • extensions

    précise si le fichier comporte des extensions composites ou non (e.g. “.tar.gz”)

    valeurs possibles: False/True
    valeur par défaut: True

  • split_extensions

    dans le cas d’une extension composite (e.g. “.tar.gz”) renvoie soit une chaine de caractère (i.e. “.tar.gz”), soit un tableau d’extensions (i.e. [“.tar”, “.gz”])

    valeurs possibles: False/True
    valeur par défaut: False (renvoie par défaut une chaine d’extensions composites)

Returns

tuple nommé avec comme attributs :
  • file_name

  • file_extension

Exemple :

# get_file_extension.py
# %%
from corelibs import lazy as lz

# %%
stem, suffix = lz.get_file_extension(r"\\file\path\file name.tar.gz")
print(f"Le nom du fichier sans extension est \"{stem}\"")
# Le nom du fichier sans extension est "file name"

# %%
print(f"Le nom de l'extension par défaut est \"{suffix}\"")
# Le nom de l'extension par défaut est ".tar.gz"

# %%
stem, suffix = lz.get_file_extension(r"\\file\path\file name.tar.gz", split_extensions=True)
print("Le nom du fichier sans extension est \"{stem}\"".format(stem=stem))
# Le nom du fichier sans extension est "file name"

# %%
print("Le tableau des extensions est \"{suffix}\"".format(suffix=suffix))
# Le tableau des extensions est "['.tar', '.gz']"

# %%
file_properties = lz.get_file_extension(r"\\file\path\file name.tar.gz", split_extensions=True)
print("Le nom du fichier sans extension est \"{stem}\"".format(stem=file_properties.file_name))
# Le nom du fichier sans extension est "file name"

# %%
print("Le tableau des extensions est \"{suffix}\"".format(suffix=file_properties.file_extension))
# Le tableau des extensions est "['.tar', '.gz']"
corelibs.lazy.get_home()

Description

Permet de retrouver le chemin de l’utilisateur (“home”)
Returns

le chemin absolu du home

Exemple :

# get_home.py
from corelibs import lazy as lz

print(lz.get_home())
corelibs.lazy.get_hostname()

Description

Récupère le nom de l’ordinateur courant
Returns

nom de l’ordinateur courant

Exemple :

# get_hostname.py
from corelibs import config, lazy as lz

hostname = lz.get_hostname()
print("Le nom de l'ordinateur actuel est \"{hostname}\"".format(hostname=hostname))
corelibs.lazy.get_locale_tab(platform_os=None, yaml_dumping=True)

Description

Liste l’ensemble des codes langues disponibles pour l’internationalisation (utilisé par corelibs.cleanse.is_datetime() ou par le builtin locale.setlocale(…) => cf. documentation officielle python)
Parameters
  • platform_os

    indique quel est la plateforme de référence à vérifier

    valeurs possibles: “Windows”, “Unix”, “All” ou “None”
    valeur par défaut: “None”

  • yaml_dumping – indique s’il faut afficher de manière lisible ou retourner la liste

Returns

liste des codes si yaml_dumping == False

Exemple :

# get_locale_tab.py
# %%
from corelibs import lazy as lz


# %%
# Affichage sous une forme lisible pour un humain
# Afficher la liste des codes langues disponibles par défaut (détection de l'OS)
lz.get_locale_tab()

# Forcer l'affichage pour toutes les plateformes disponibles
lz.get_locale_tab(platform_os="all")

# Afficher la liste pour Windows
lz.get_locale_tab(platform_os="Windows")

# Afficher la liste pour Unix
lz.get_locale_tab(platform_os="Unix")


# %%
# Récupération de la liste dans une variable
locale_tab_list = lz.get_locale_tab(platform_os="all", yaml_dumping=False)
print(locale_tab_list["Windows"]["fr"])  # affichera français (France)
corelibs.lazy.get_module_name()

Description

Retourne le nom du module courant
Returns

nom module courant

Exemple :

# get_module_name.py
from corelibs import lazy as lz

module_name = lz.get_module_name()
print("Le nom du module python est \"{module_name}\"".format(module_name=module_name))
corelibs.lazy.get_module_path()

Description

Retourne le chemin du script/programme python courant
Returns

os.path.realpath(sys.argv[0])
ou
os.path.dirname(os.path.realpath(sys.argv[0])))

Exemple :

# get_module_path.py
from corelibs import lazy as lz

path = lz.get_module_path()
print("Le chemin du programme python est \"{path}\"".format(path=path))

Terminal :

$ python get_module_path.py
Le chemin du programme python est "C:\Users\M47624\corelibs\tests\lazy"
corelibs.lazy.get_path_docs_dir(location=None)

Description

Retourne le chemin absolu du répertoire des docs/specs, nommé par défaut “__DOCS__” cf. corelibs.lazy.mkdir() pour plus de détails.
Parameters

location

la location du répertoire racine contenant le répertoire des docs/specs.

valeur par défaut: le chemin retourné par corelibs.lazy.get_module_path()

Returns

chemin absolu du répertoire “__DOCS__” (si existe, e.g. “C:\Users\M47624\__DOCS__”)
ou
chaîne vide (sinon)

Exemple :

# get_path_docs_dir.py
from corelibs import config, lazy as lz

root_path = r"D:\OneDrive\Documents\_TEST_\PARENTS\INEXISTANTS"

print(lz.get_path_docs_dir(root_path))  # retourne rien

lz.mkdir(
    root_path,
    dir_scaffolding={
        "input": {  # dossier contenant toutes les données "entrées"
            "name": "__R2 D2__",
            "make": False
        },
        "output": {  # dossier contenant toutes les données "sorties"
            "name": "__MY_OUTPUTS__",
            "make": False
        },
        "logs": {  # dossier contenant toutes les sorties "logs"
            "name": "__MY-LOGS__",
            "make": False
        },
        "docs": {  # dossier contenant toutes les documentations/specs liées au projet
            "name": "__SPECS__",
            "make": True
        },
    },
    verbose=False
)
print(lz.get_path_docs_dir(root_path))  # retourne "D:\OneDrive\Documents\_TEST_\PARENTS\INEXISTANTS\__SPECS__"
corelibs.lazy.get_path_input_dir(location=None)

Description

Retourne le chemin absolu du répertoire des entrées, nommé par défaut “__INPUTS__” cf. corelibs.lazy.mkdir() pour plus de détails.
Parameters

location

la location du répertoire racine contenant le répertoire des entrées.

valeur par défaut: le chemin retourné par corelibs.lazy.get_module_path()

Returns

chemin absolu du répertoire “__INPUTS__” (si existe, e.g. “C:\Users\M47624\__INPUTS__”)
ou
chaîne vide (sinon)

Exemple :

# get_path_input_dir.py
from corelibs import config, lazy as lz

root_path = r"D:\OneDrive\Documents\_TEST_\PARENTS\INEXISTANTS"

print(lz.get_path_input_dir(root_path))  # retourne rien

lz.mkdir(
    root_path,
    dir_scaffolding={
        "input": {  # dossier contenant toutes les données "entrées"
            "name": "__R2 D2__",
            "make": True
        },
        "output": {  # dossier contenant toutes les données "sorties"
            "name": "__MY_OUTPUTS__",
            "make": False
        },
        "logs": {  # dossier contenant toutes les sorties "logs"
            "name": "__MY-LOGS__",
            "make": False
        },
        "docs": {  # dossier contenant toutes les documentations/specs liées au projet
            "name": "__DOCS__",
            "make": False
        },
    },
    verbose=False
)
print(lz.get_path_input_dir(root_path))  # retourne "D:\OneDrive\Documents\_TEST_\PARENTS\INEXISTANTS\__R2 D2__"
corelibs.lazy.get_path_logs_dir(location=None)

Description

Retourne le chemin absolu du répertoire des logs, nommé par défaut “__LOGS__” cf. corelibs.lazy.mkdir() pour plus de détails.
Parameters

location

la location du répertoire racine contenant le répertoire des logs.

valeur par défaut: le chemin retourné par corelibs.lazy.get_module_path()

Returns

chemin absolu du répertoire “__LOGS__” (si existe, e.g. “C:\Users\M47624\__LOGS__”)
ou
chaîne vide (sinon)

Exemple :

# get_path_logs_dir.py
from corelibs import config, lazy as lz

root_path = r"D:\OneDrive\Documents\_TEST_\PARENTS\INEXISTANTS"

print(lz.get_path_logs_dir(root_path))  # retourne rien

lz.mkdir(
    root_path,
    dir_scaffolding={
        "input": {  # dossier contenant toutes les données "entrées"
            "name": "__R2 D2__",
            "make": False
        },
        "output": {  # dossier contenant toutes les données "sorties"
            "name": "__MY_OUTPUTS__",
            "make": False
        },
        "logs": {  # dossier contenant toutes les sorties "logs"
            "name": "__MY-LOGS__",
            "make": True
        },
        "docs": {  # dossier contenant toutes les documentations/specs liées au projet
            "name": "__DOCS__",
            "make": False
        },
    },
    verbose=False
)
print(lz.get_path_logs_dir(root_path))  # retourne "D:\OneDrive\Documents\_TEST_\PARENTS\INEXISTANTS\__MY_LOGS__"
corelibs.lazy.get_path_output_dir(location=None)

Description

Retourne le chemin absolu du répertoire des sorties, nommé par défaut “__OUTPUTS__” cf. corelibs.lazy.mkdir() pour plus de détails.
Parameters

location

la location du répertoire racine contenant le répertoire des sorties.

valeur par défaut: le chemin retourné par corelibs.lazy.get_module_path()

Returns

chemin absolu du répertoire “__OUTPUTS__” (si existe, e.g. “C:\Users\M47624\__OUTPUTS__”)
ou
chaîne vide (sinon)

Exemple :

# get_path_output_dir.py
from corelibs import config, lazy as lz

root_path = r"D:\OneDrive\Documents\_TEST_\PARENTS\INEXISTANTS"

print(lz.get_path_output_dir(root_path))  # retourne rien

lz.mkdir(
    root_path,
    dir_scaffolding={
        "input": {  # dossier contenant toutes les données "entrées"
            "name": "__R2 D2__",
            "make": False
        },
        "output": {  # dossier contenant toutes les données "sorties"
            "name": "__MY_OUTPUTS__",
            "make": True
        },
        "logs": {  # dossier contenant toutes les sorties "logs"
            "name": "__MY-LOGS__",
            "make": False
        },
        "docs": {  # dossier contenant toutes les documentations/specs liées au projet
            "name": "__DOCS__",
            "make": False
        },
    },
    verbose=False
)
print(lz.get_path_output_dir(root_path))  # retourne "D:\OneDrive\Documents\_TEST_\PARENTS\INEXISTANTS\__MY_OUTPUTS__"
corelibs.lazy.get_path_scaffold_directories(location=None)

Description

Retourne les chemins absolus de tous les répertoires créés cf. corelibs.lazy.mkdir() pour plus de détails.
Parameters

location

la location du répertoire racine contenant les répertoires modèles.

valeur par défaut: le chemin retourné par corelibs.lazy.get_module_path()

Returns

tuple nommé avec comme attributs :
  • docs : chemin absolu du répertoire “__DOCS__” (si existe)

  • input : chemin absolu du répertoire “__INPUTS__” (si existe)

  • output : chemin absolu du répertoire “__OUTPUTS__” (si existe)

  • logs : chemin absolu du répertoire “__LOGS__” (si existe)

Exemple :

# get_path_scaffold_directories.py
# %%
from corelibs import config, lazy as lz

root_path = r"D:\OneDrive\Documents\_TEST_\PARENTS\INEXISTANTS"

print(lz.get_path_scaffold_directories(root_path))  # retourne None

lz.mkdir(
    root_path,
    dir_scaffolding={
        "input": {  # dossier contenant toutes les données "entrées"
            "name": "__R2 D2__",
            "make": True
        },
        "output": {  # dossier contenant toutes les données "sorties"
            "name": "__MY_OUTPUTS__",
            "make": True
        },
        "logs": {  # dossier contenant toutes les sorties "logs"
            "name": "__MY-LOGS__",
            "make": True
        },
        "docs": {  # dossier contenant toutes les documentations/specs liées au projet
            "name": "__DOCUMENTATIONS__",
            "make": True
        },
    },
    verbose=False
)

scaffold_dir_path = lz.get_path_scaffold_directories(root_path)
print(scaffold_dir_path)

# récupréer les chemins des docs
print(scaffold_dir_path.docs)

# récupréer les chemins des inputs
print(scaffold_dir_path.input)

# récupréer les chemins des outputs
print(scaffold_dir_path.output)

# récupréer les chemins des logs
print(scaffold_dir_path.logs)

# %%
# si le définition est écrasée, alors le get_path_scaffold_directories() va tenter de retrouver les nouvelles
# informations, par exemple ci-dessous, le nouveau nom du répertoire "input" s'appellera "__R2 D2__" et ainsi de suite
config.DEFAULT_DIR_SCAFFOLDING = {
    "input": {  # dossier contenant toutes les données "entrées"
        "name": "__R2 D2__",
        "make": False
    },
    "output": {  # dossier contenant toutes les données "sorties"
        "name": "__MY_OUTPUTS__",
        "make": False
    },
    "logs": {  # dossier contenant toutes les sorties "logs"
        "name": "__MY-LOGS__",
        "make": True
    },
    "docs": {  # dossier contenant toutes les documentations/specs liées au projet
        "name": "__DOCS__",
        "make": False
    },
}

print(lz.get_path_scaffold_directories(root_path))  # affichera
# ScaffoldDir(input='D:\\OneDrive\\Documents\\_TEST_\\PARENTS\\INEXISTANTS\\__R2 D2__', output='D:\\OneDrive\\Documents\\_TEST_\\PARENTS\\INEXISTANTS\\__MY_OUTPUTS__', logs='D:\\OneDrive\\Documents\\_TEST_\\PARENTS\\INEXISTANTS\\__MY-LOGS__', docs='')
# "docs" est vide car nous cherchons un nom "__DOCS__" alors que nous avons créé "__DOCUMENTATIONS__"
corelibs.lazy.get_timestamp(timestamp_format='DT', display_ms=False, only_ms=False)

Description

Retourne un timestamp normalisé ; utile pour suffixer les noms des fichiers
Parameters
  • timestamp_format – timestamp_format souhaité du timestamp | valeurs possibles: DT, D, T, NOW, GD ou GT | valeur par défaut: DT

  • display_ms

    afficher ou non les milli secondes

    valeurs possibles: False/True
    valeur par défaut: False

  • only_ms

    récupère seulement les millièmes de secondes (pratique si besoin d’un seed)

    valeurs possibles: False/True
    valeur par défaut: False

Returns

timestamp, selon timestamp_format :
* YYYYMMDD_HHMMSS.SSSSSS
* YYYYMMDD_HHMMSS
* ou SSSSSS

Exemple :

# get_timestamp.py
# %%
from corelibs import lazy as lz


# %%
timestamp = lz.get_timestamp()
print("Le timestamp sans milli secondes est {timestamp}".format(timestamp=timestamp))
# Le timestamp sans milli secondes est 20201209_181434

# %%
timestamp = lz.get_timestamp(only_ms=True)
print("Le timestamp en milli secondes est {timestamp}".format(timestamp=timestamp))
# Le timestamp en milli secondes est 096248

# %%
timestamp = lz.get_timestamp(display_ms=True)
print("Le timestamp avec milli secondes est {timestamp}".format(timestamp=timestamp))
# Le timestamp avec milli secondes est 20201209_181434.097248

# %%
timestamp = lz.get_timestamp(timestamp_format="D")
print("Le timestamp avec la date seulement est {timestamp}".format(timestamp=timestamp))
# Le timestamp avec la date seulement est 20201209

# %%
timestamp = lz.get_timestamp(timestamp_format="T")
print("Le timestamp avec l'heure seulement est {timestamp}".format(timestamp=timestamp))
# Le timestamp avec l'heure seulement est 181434

# %%
timestamp = lz.get_timestamp(timestamp_format="NOW")
print("Le timestamp avec la date et l'heure sans retraitement est {timestamp}".format(timestamp=timestamp))
# Le timestamp avec la date et l'heure sans retraitement est 2020-12-09 18:14:34

# %%
timestamp = lz.get_timestamp(timestamp_format="GD")
print("Le timestamp avec la date sans retraitement est {timestamp}".format(timestamp=timestamp))
# Le timestamp avec la date sans retraitement est 2020-12-09

# %%
timestamp = lz.get_timestamp(timestamp_format="GT")
print("Le timestamp avec l'heure sans retraitement est {timestamp}".format(timestamp=timestamp))
# Le timestamp avec l'heure sans retraitement est 18:14:34

Terminal :

$ python get_timestamp.py
Le timestamp sans milli secondes est 20201025_182141
Le timestamp en milli secondes est 457668
Le timestamp avec milli secondes est 20201025_182141.457668
Le timestamp avec la date seulement est 20201025
Le timestamp avec l'heure seulement est 182141
Le timestamp avec la date et l'heure sans retraitement est 2020-10-25 18:21:41
Le timestamp avec la date sans retraitement est 2020-10-25
Le timestamp avec l'heure sans retraitement est 18:21:41
corelibs.lazy.get_username()

Description

Récupère le nom de l’utilisateur courant
Returns

nom de l’utilisateur courant

Exemple :

# get_username.py
from corelibs import config, lazy as lz

username = lz.get_username()
print("Le nom de l'utilisateur actuel est \"{username}\"".format(username=username))
corelibs.lazy.is_file_exists(file_path, is_dir=False, ignore_errors=False)

Description

Vérifie si le fichier existe ou non
Parameters
  • file_path – chemin du fichier/répertoire

  • is_dir

    indique si la vérification porte sur un dossier ou non

    valeurs possibles: False/True
    valeur par défaut: False (la vérification se fait sans distinction)

  • ignore_errors

    forcer l’exécution lorsqu’une erreur est levée

    valeurs possibles: False/True
    valeur par défaut: False (cf. DEFAULT_IGNORE_ERROR dans Installation & Mise à jour)

Returns

True/False

Exemple :

# is_file_exists.py
# %%
from corelibs import lazy as lz


def test(file_test):
    if lz.is_file_exists(file_test):
        print("Le fichier \"{file_test}\" existe".format(file_test=file_test))
    else:
        print("Le fichier \"{file_test}\" n'existe pas".format(file_test=file_test))


file = r"D:\OneDrive\Documents\_TEST_"
test(file)

file = r"D:\OneDrive\Documents\_TEST_\__LOGS__\Fichier.txt"
test(file)

# %%
file = r"D:\OneDrive\Documents\_TEST_"
if lz.is_file_exists(file, is_dir=True):
    print("Le fichier \"{file}\" existe et est un répertoire".format(file=file))

# %%
file = r"D:\OneDrive\Documents\_TEST_\Fichier.txt"
if lz.is_file_exists(file):
    print("Le fichier \"{file}\" existe".format(file=file))

# %%
file = r"D:\OneDrive\Documents\_TEST_\Fichier.txt"
if not lz.is_file_exists(file, is_dir=True):
    print("Le fichier \"{file}\" n'est pas un répertoire".format(file=file))

Terminal :

$ python get_abspath.py
Le fichier "D:\OneDrive\Documents\_TEST_\__LOGS__" existe
Le fichier "D:\OneDrive\Documents\_TEST_\__LOGS__\Fichier.txt" existe
Le fichier "D:\OneDrive\Documents\_TEST_\__LOGS__\Fichier.txt" existe et est un répertoire
Le fichier "D:\OneDrive\Documents\_TEST_\__LOGS__\Fichier.txt" existe et n'est pas un répertoire
corelibs.lazy.is_interactive_python()

Description

Permet de savoir si l’environnement sur lequel est exécuté le script python est un terminal Python interactive ou non (i.e. vs console ou terminal standard)
Returns

Booléen: False/True

Exemple :

# is_interactive_python.py
from corelibs import lazy as lz

print(lz.is_interactive_python())
corelibs.lazy.is_jupyter()

Description

Permet de savoir si l’environnement sur lequel est exécuté le script python est Jupyter Notebook ou un terminal QTConsole Jupyter
Returns

Booléen: False/True

Exemple :

# is_jupyter.py
from corelibs import lazy as lz

print(lz.is_jupyter())
corelibs.lazy.is_namedtuple_instance(obj)

Description

Permet de déterminer si l’objet passé en argument est une instance de tuple nommé.
Parameters

obj – un tuple nommé

Returns

Booléen: False/True

Exemple :

# is_namedtuple_instance.py
from collections import namedtuple
from corelibs import lazy as lz


# création d'un objet PuPuce tuple nommé
PuPuce = namedtuple("PuPuce", ["name", "age"])
kim = PuPuce(
    name="Kim",
    age=6
)
print(kim)  # affiche bien PuPuce(name='Kim', age=6)

print(lz.is_namedtuple_instance(kim))  # affiche bien True

print(lz.is_namedtuple_instance("Coucou Kim"))  # affiche False
corelibs.lazy.is_platform(platform_os='Windows', ignore_errors=False)

Description

Permet de vérifier le système d’exploitation sur lequel est lancé le script python
Parameters
  • platform_os

    indique quel est la plateforme de référence à vérifier .

    valeurs possibles: “Windows”, “Linux” ou “OSX”
    valeur par défaut: “Windows”

  • ignore_errors

    forcer l’exécution lorsqu’une erreur est levée

    valeurs possibles: False/True
    valeur par défaut: False (cf. DEFAULT_IGNORE_ERROR dans Installation & Mise à jour)

Returns

Booléen: False/True

Exemple :

# is_platform.py
from corelibs import config, lazy as lz

print(lz.is_platform("Windows"))  # vérfie si l'OS est Windows ou non...
print(lz.is_platform("Linux"))
print(lz.is_platform("OSX"))

print(lz.is_platform("INCONNUE"))
corelibs.lazy.is_stdin()

Description

Permet de savoir si l’environnement sur lequel est exécuté le script python est un terminal standard ou non
Returns

Booléen: False/True

corelibs.lazy.is_validated_schema(data_2_validate, schema, is_dict_schema=True, verbose=False, ignore_errors=False)

Description

Vérifie si une donnée est conforme au schéma descriptif. La donnée à éprouver est par défaut un dictionnaire autrement, c’est un fichier de configuration YAML.
Parameters
  • data_2_validate

    donnée à valider

    dictionnaire ou tuple (si is_dict_schema == True)
    ou
    chemin absolu du fichier YAML à valider (si is_dict_schema == False)

  • schema

    nom du schéma de référence

    schéma (si is_dict_schema == True)
    ou
    chemin absolu du schéma YAML permettant la validation (si is_dict_schema == False)

  • is_dict_schema

    indique si le schéma est un dictionnaire ou un fichier YAML

    valeurs possibles: False/True
    valeur par défaut: True

  • verbose

    afficher ou non les messages d’info/alertes

    valeurs possibles: False/True
    valeur par défaut: DEFAULT_VERBOSE (cf. Installation & Mise à jour)

  • ignore_errors

    forcer l’exécution lorsqu’une erreur est levée

    valeurs possibles: False/True
    valeur par défaut: False (cf. DEFAULT_IGNORE_ERROR dans Installation & Mise à jour)

Returns

Booléen: False/True

Exemple :

# is_validated_schema.py
# %%
import schema as sc

from corelibs import lazy as lz

########################################################################################################################
# TEST Schéma DICT (défaut)
########################################################################################################################
# Test schéma byte format size
SCHEMA_DEFAULT_BYTE_SIZE_FORMAT = sc.Schema({
    sc.Optional(sc.And(str, lambda s: s in (
        # clé parmi les noms définis ci-dessous
        "octet", "Ko", "Mo", "Go", "To"
    ))): {
        "min_size": sc.Or(int, float)
    }
})
# Test valide
DEFAULT_BYTE_SIZE_FORMAT_OK = {
    "octet": {"min_size": 0},
    "Ko": {"min_size": 1},
    "Mo": {"min_size": 1},
    "Go": {"min_size": 1},
    "To": {"min_size": 0.5}
}
lz.is_validated_schema(DEFAULT_BYTE_SIZE_FORMAT_OK, SCHEMA_DEFAULT_BYTE_SIZE_FORMAT, verbose=True)
# Test invalide
DEFAULT_BYTE_SIZE_FORMAT_KO = {
    "octet": {"min_size": "Hello"},
    "Ko": {"min_size": "Kim"},
    "Mo": {"min_size": "Marie"},
    "Go": {"min_size": "Adélie"},
    "To": {"min_size": 7}
}
lz.is_validated_schema(DEFAULT_BYTE_SIZE_FORMAT_KO, SCHEMA_DEFAULT_BYTE_SIZE_FORMAT, verbose=True)

# %%
# Test schéma scaffolding
SCHEMA_DIR_SCAFFOLDING = sc.Schema({
    sc.Optional(sc.And(str, lambda s: s in (
        # clé parmi les noms définis ci-dessous
        "input", "output", "logs", "docs"
    ))): {
        "name": sc.Regex(
            r"^__[a-zA-Z0-9 _-]+__$"
        ),
        "make": bool
    }
})
# Test valide
DIR_SCAFFOLDING_OK = {
    "input": {  # dossier contenant toutes les données "entrées"
        "name": "__MY_INPUTS__",
        "make": True
    },
    "output": {  # dossier contenant toutes les données "sorties"
        "name": "__MY_OUTPUTS__",
        "make": True
    },
    "logs": {  # dossier contenant toutes les sorties "logs"
        "name": "__MY-LOGS__",
        "make": True
    },
}
lz.is_validated_schema(DIR_SCAFFOLDING_OK, SCHEMA_DIR_SCAFFOLDING, verbose=True)
# Test invalide
DIR_SCAFFOLDING_KO = {
    "input": {  # dossier contenant toutes les données "entrées"
        "name": "__R2 D2__",
        "make": False
    },
    "output": {  # dossier contenant toutes les données "sorties"
        "name": "__MY_OUTPUTS__",
        "make": "Coucou"
    },
    "logs": {  # dossier contenant toutes les sorties "logs"
        "name": "__MY-LOGS__",
        "make": True
    },
}
lz.is_validated_schema(DIR_SCAFFOLDING_KO, SCHEMA_DIR_SCAFFOLDING)

# %%
# Test schéma styles des champs
SCHEMA_FIELD_STYLES = sc.Schema({
    sc.Optional(sc.And(str, lambda s: s in (
        "asctime", "hostname", "username", "levelname", "name", "programname"
    ))): {
        "color": sc.Or(sc.And(int, lambda n: 0 <= n <= 255),
                       sc.And(str, lambda s: s in (
                           "black", "blue", "cyan", "green",
                           "magenta", "red", "white", "yellow"
                       ))),
        sc.Optional(sc.And(str, sc.Use(str.lower), lambda s: s in (
            "bold", "bright", "faint"
        ))): bool,
    }
})
# Test valide...
FIELD_STYLES_OK = {
    "asctime": {"color": 242, "bright": True},
    "hostname": {"color": "magenta"},
    "username": {"color": "yellow"},
    "levelname": {"color": 242, "bright": True},
    "name": {"color": "blue"},
    "programname": {"color": "cyan"}
}
lz.is_validated_schema(FIELD_STYLES_OK, SCHEMA_FIELD_STYLES)
# Test invalide...
FIELD_STYLES_KO = {
    "asctimeZ": {"color": 242, "bright": True},
    "hostname": {"color": "white"},
    "username": {"color": "yellow"},
    "levelname": {"color": 242, "bright": False},
    "name": {"color": "blue"},
    "programname": {"color": "cyan"}
}
lz.is_validated_schema(FIELD_STYLES_KO, SCHEMA_FIELD_STYLES)

# %%
# Test schéma styles des niveaux d'alertes
SCHEMA_LEVEL_STYLES = sc.Schema({
    sc.Optional(sc.And(str, lambda s: s in (
        "critical", "error", "warning", "debug", "info", "notice", "spam", "success", "verbose"
    ))): {
        "color": sc.Or(sc.And(int, lambda n: 0 <= n <= 255),
                       sc.And(str, lambda s: s in (
                           "black", "blue", "cyan", "green",
                           "magenta", "red", "white", "yellow"
                       ))),
        sc.Optional(sc.And(str, sc.Use(str.lower), lambda s: s in (
            "background"
        ))): sc.Or(sc.And(int, lambda n: 0 <= n <= 255),
                   sc.And(str, lambda s: s in (
                       "black", "blue", "cyan", "green",
                       "magenta", "red", "white", "yellow"
                   ))),
    }
})
# Test valide...
LEVEL_STYLES_OK = {
    "critical": {"color": "white", "background": "red"},
    "verbose": {"color": "white"},
}
lz.is_validated_schema(LEVEL_STYLES_OK, SCHEMA_LEVEL_STYLES)
# Test invalide...
LEVEL_STYLES_KO = {
    "WRONG_KEY_critical": {"color": "white", "background": "red"}
}
lz.is_validated_schema(LEVEL_STYLES_KO, SCHEMA_LEVEL_STYLES)

# %%
# Test schéma format d'affichage des logs
SCHEMA_LOG_FORMAT = sc.Regex(
    r"^([ <>•@:=$~{}\(\)\[\]\w\d\-\.]*"
    + r"%\((\b(asctime|created|filename|funcName|levelname|levelno|lineno|message|module|msecs|name"
    + r"|pathname|process|processName|relativeCreated|thread|threadName|username|hostname)\b)\)"
    + r"\d*[sd]{1,1}[ <>•@:=$~{}\(\)\[\]\w\d\-\.]*)*$"
)
# Test valide...
LOG_FORMAT_OK = \
    "> %(asctime)s %(username)s@%(hostname)s - %(name)s" \
    + "[P.%(process)d - T.%(thread)d - L.%(lineno)05d] • %(levelname)13s %(message)s"
lz.is_validated_schema(LOG_FORMAT_OK, SCHEMA_LOG_FORMAT)
# Test invalide...
LOG_FORMAT_KO = "%(asctime)s %(username)s@%(hostname)s %(name)s[%(process)d] • %(_levelname_)13s %(message)s"
lz.is_validated_schema(LOG_FORMAT_KO, SCHEMA_LOG_FORMAT)

# %%
# Test schéma format d'affichage timestamp des logs
SCHEMA_LOG_DATE_FORMAT = sc.Regex(r"^(%(%|\b[aAwdbBmyYHIpMSfzZjUWcxXGuV]\b)[ \/\-:]*)*$")
# Test valide...
LOG_DATE_FORMAT_OK = "%Y/%m/%d %H:%M:%S"
lz.is_validated_schema(LOG_DATE_FORMAT_OK, SCHEMA_LOG_DATE_FORMAT)
# Test invalide...
LOG_DATE_FORMAT_KO = "%Y-%m-%d %HH : %M : %S"
lz.is_validated_schema(LOG_DATE_FORMAT_KO, SCHEMA_LOG_DATE_FORMAT)

# %%
########################################################################################################################
# TEST Schéma YAML
########################################################################################################################
lz.is_validated_schema(
    r"D:\OneDrive\Documents\[PYTHON_PROJECTS]\corelibs\tests\lazy\conf_test.yaml",
    r"D:\OneDrive\Documents\[PYTHON_PROJECTS]\corelibs\tests\lazy\schema_conf_test.yaml",
    is_dict_schema=False
)

Exemple Schéma YAML :

# schema_conf_test.yaml
list(include("person"), min=1)
---
person:
  first name: str()
  last name: str()
  age: int(max=130, required=False)

Exemple fichier YAML à valider avec le schéma descriptif YAML :

- first name: "Kim Marie Adélie"
  last name: "TRUONG"
  age: 6

- first name: "Anne"
  last name: "TRUONG"

- first name: "Michel"
  last name: "TRUONG"
  age: 231  # erreur car age supérieur à 130 qui est le maximum autorisé
  unknown key: "yeah!"  # erreur car clé inconnue

- mt: False  # erreur car clé inconnu dans un nouveau bloc

Terminal :

$ python is_validated_schema.py  #illustration indicative non nécessairement représentative du code en exemple
_images/is_well_formatted_schema_log.png
corelibs.lazy.merge_dictionaries(merged_dictionary, dictionary_2_merge)

Description

Permet de fusionner 2 dictionnaires ensemble
/!\ ATTENTION /!\ L’ordre des dictionnaires en argument est important!
Parameters
  • merged_dictionary – dictionnaire conteneur destiné à recevoir le résultat de la fusion

  • dictionary_2_merge – dictionnaire à fusionner

Returns

dictionnaire résultat de la fusion
ou
None

Exemple :

# merge_dictionaries.py
# %%
from corelibs import lazy as lz

# %%
x = None
y = {"prenom": "Kim", "age": 6}
z = lz.merge_dictionaries(x, y)
print(z)  # {'prenom': 'Kim', 'age': 6}

# %%
# inversion
z = lz.merge_dictionaries(y, x)
print(z)  # None

# %%
x = {"a": 1, "b": 2}
y = {"b": 10, "c": 11}
z = lz.merge_dictionaries(x, y)
print(z)  # {"a": 1, "b": 10, "c": 11}

# %%
x = {"b": 10, "c": 11}
y = {"a": 1, "b": {"b1": "hello", "b2": 3}}
z = lz.merge_dictionaries(x, y)
print(z)  # {"b": {"b1": "hello", "b2": 3}, "c": 11, "a": 1}

# %%
x = {"b": {"b1": "hello", "b2": 3}, "c": 11}
y = {"a": 1, "b": {"b1": "Kim", "b2": 6}}
z = lz.merge_dictionaries(x, y)
print(z)  # {"b": {"b1": "Kim", "b2": 6}, "c": 11, "a": 1}

# %%
DICT_USER = {
    "asctime": {"color": "BLACK"},
    "levelname": {"color": "WHITE"}
}

DICT_REF = {
    "asctime": {"color": 242, "bright": True},
    "hostname": {"color": "magenta"},
    "username": {"color": "yellow"},
    "levelname": {"color": 242, "bright": True},
    "name": {"color": "blue"},
    "programname": {"color": "cyan"}
}

MERGED_DICT = lz.merge_dictionaries(DICT_REF, DICT_USER)
print(MERGED_DICT)  # {
#   "asctime": {"color": "BLACK", "bright": True},
#   "hostname": {"color": "magenta"},
#   "username": {"color": "yellow"},
#   "levelname": {"color": "WHITE", "bright": True},
#   "name": {"color": "blue"},
#   "programname": {"color": "cyan"}
# }

corelibs.lazy.mkdir(location=None, make_scaffolding=True, dir_scaffolding={'docs': {'make': True, 'name': '__DOCS__'}, 'input': {'make': True, 'name': '__INPUTS__'}, 'logs': {'make': True, 'name': '__LOGS__'}, 'output': {'make': True, 'name': '__OUTPUTS__'}}, verbose=False, ignore_errors=False)

Description

Créer un répertoire standard ou une liste de répertoires de manière récursive ; si le ou les parents n’existent pas, ils seront créés (dans le cas d’une liste de répertoires cela permet de factoriser les instructions).
Par défaut, lorsque corelibs.lazy.mkdir() est appelé, 4 répertoires en plus sont créés :
  • 1 pour recevoir les fichiers logs, nommé par défaut “__LOGS__”

  • 1 pour recevoir les sorties, nommé par défaut “__OUTPUTS__”

  • 1 pour recevoir les entrées, nommé par défaut “__INPUTS__”

  • 1 pour recevoir les documentations et/ou spécifications, nommé par défaut “__DOCS__”

Note

Les noms respectifs des répertoires modèles sont gérés par le dictionnaire

config.DEFAULT_DIR_SCAFFOLDING (cf. Installation & Mise à jour pour les détails.)

Il est possible de modifier les noms et/ou la création des répertoires modèles en chargeant un nouveau dictionnaire lors de l’appel de corelibs.lazy.mkdir(), directement en argument ou via un écrasement de la constante DEFAULT_DIR_SCAFFOLDING.

Parameters
  • location

    la location du répertoire à créer.

    valeurs possibles: chemin absolu ou tuple/tableau de chemins absolus
    valeur par défaut: le chemin retourné par corelibs.lazy.get_module_path()

  • make_scaffolding

    indique s’il faut ou non créer les dossiers “modèle”

    valeurs possibles: Dictionnaire
    valeur par défaut: DEFAULT_DIR_SCAFFOLDING (cf. Installation & Mise à jour)

  • dir_scaffolding

    dictionnaire définissant les noms des dossiers “modèle” et s’il faut ou non les créer unitairement

    valeurs possibles: False/True
    valeur par défaut: True

  • verbose

    afficher ou non les messages d’info/alertes

    valeurs possibles: False/True
    valeur par défaut: DEFAULT_VERBOSE (cf. Installation & Mise à jour)

  • ignore_errors

    forcer l’exécution lorsqu’une erreur est levée

    valeurs possibles: False/True
    valeur par défaut: False (cf. DEFAULT_IGNORE_ERROR dans Installation & Mise à jour)

Returns

rien…

Exemple :

# mkdir.py
# %%
from corelibs import lazy as lz


# %%
# création par défaut où se trouve l'emplacement du programme `mkdir.py`
lz.mkdir()

# %%
# création d'un répertoire standard avec les parents si n'existe pas, sans la structure "modèle"
lz.mkdir(
    location=r"D:\OneDrive\Documents\_TEST_\PARENTS\INEXISTANTS\DOSSIER_STANDARD",
    make_scaffolding=False,
    verbose=True
)

# %%
# création avec 4 dossiers par défaut de la structure "modèle"
# à l'emplacement "D:\OneDrive\Documents\_TEST_\PARENTS\INEXISTANTS\DOSSIER_STANDARD"
lz.mkdir(location=r"D:\OneDrive\Documents\_TEST_\PARENTS\DOSSIERS_SCAFFOLD", verbose=True)

# %%
# création personnalisée avec un seul dossier "__MY-LOGS__"
# à l'emplacement "D:\OneDrive\Documents\_TEST_\PARENTS\INEXISTANTS\DOSSIER_STANDARD"
lz.mkdir(
    location=r"D:\OneDrive\Documents\_TEST_\PARENTS\INEXISTANTS\DOSSIER_STANDARD",
    dir_scaffolding={
        "input": {  # dossier contenant toutes les données "entrées"
            "name": "__R2 D2__",
            "make": False
        },
        "output": {  # dossier contenant toutes les données "sorties"
            "name": "__MY_OUTPUTS__",
            "make": False
        },
        "logs": {  # dossier contenant toutes les sorties "logs"
            "name": "__MY-LOGS__",
            "make": True
        },
        "docs": {  # dossier contenant toutes les documentations/specs liées au projet
            "name": "__DOCS__",
            "make": False
        },
    },
    verbose=True
)

# %%
# création d'une liste de répertoires standards (sans les répertoires modèles) ayant pour structure
# ...DOSSIER_T42020
#    |_Dossier A
#    |_Dossier B
#      |_SDossier B1
#      |_SDossier B2
lz.mkdir(
    location=(
        r"D:\OneDrive\Documents\_TEST_\DOSSIER_T42020\Dossier A",
        r"D:\OneDrive\Documents\_TEST_\DOSSIER_T42020\Dossier B\SDossier B1",
        r"D:\OneDrive\Documents\_TEST_\DOSSIER_T42020\Dossier B\SDossier B2",
    ),
    make_scaffolding=False,
    verbose=True
)  # dans la mesure où les dossiers parents sont créés si n'existent pas, une factorisation est possible...

# %%
# création d'une liste de répertoires standards (avec les répertoires modèles personnalisés) ayant pour structure
# ...DOSSIER_T12021
#    |_Dossier A
#      |__MY_INPUTS__
#      |__MY_OUTPUTS__
#      |__MY_LOGS__
#    |_Dossier B
#      |_SDossier B1
#        |_ +3 DOSSIERS MODELES PERSONNALISÉS
#      |_SDossier B2
#        |_ +3 DOSSIERS MODELES PERSONNALISÉS
lz.mkdir(
    location=(
        r"D:\OneDrive\Documents\_TEST_\DOSSIER_T12021\Dossier A",
        r"D:\OneDrive\Documents\_TEST_\DOSSIER_T12021\Dossier B\SDossier B1",
        r"D:\OneDrive\Documents\_TEST_\DOSSIER_T12021\Dossier B\SDossier B2",
    ),
    dir_scaffolding={
        "input": {  # dossier contenant toutes les données "entrées"
            "name": "__MY_INPUTS__",
            "make": True
        },
        "output": {  # dossier contenant toutes les données "sorties"
            "name": "__MY_OUTPUTS__",
            "make": True
        },
        "logs": {  # dossier contenant toutes les sorties "logs"
            "name": "__MY_LOGS__",
            "make": True
        },
        "docs": {  # dossier contenant toutes les documentations/specs liées au projet
            "name": "__DOCS__",
            "make": False
        },
    },
    verbose=True
)  # cette construction n'a aucun intérêt fonctionnel mais cela est possible...

Terminal :

$ python mkdir.py  #illustration indicative non nécessairement représentative du code en exemple
_images/mkdir_log.png
corelibs.lazy.move(source, destination)

Description

Permet de déplacer un ou des fichiers vers une nouvelle destination ou des nouvelles destinations. Les fichiers sont au sens Unix du terme (i.e. soit fichier régulier, soit répertoire)
Parameters
  • source – indique l’emplacement source du ou des fichiers à déplacer avec le(s) chemin(s) absolu(s), avec ou sans schéma.

  • destination – indique l’emplacement destination du ou des fichiers à déplacer avec le(s) chemin(s) absolu(s).

Returns

rien…

Exemple :

# move.py
# %%
from corelibs import lazy as lz


# %%
# Création dossier destination
repertoire_destination = r"D:\OneDrive\Documents\_TEST_\_NEW_DESTINATION_"
lz.mkdir(repertoire_destination, make_scaffolding=False)

# %%
# Déplacement simple de fichier standard, sans renommage
lz.move(r"\\wsl$\Ubuntu-20.04\root\.zsh_history", repertoire_destination)  # chemin réseau...

# %%
# Déplacement simple de fichier standard, avec renommage
lz.move(r"D:\OneDrive\Documents\_TEST_\_éèçàoöôîïêëùûü;.txt", repertoire_destination + "\\nouveau_nom.txt")

# %%
# Déplacement simple de répertoire standard, sans renommage
lz.move(r"D:\OneDrive\Documents\_TEST_\__R2D2-LOGS__", repertoire_destination)

# %%
# Déplacement simple de répertoire standard, avec renommage
lz.move(r"D:\OneDrive\Documents\_TEST_\__R2D2-LOGS__", repertoire_destination + "\\__R2D2__")

# %%
# Déplacement via mode modèle
lz.move(r"D:\OneDrive\Documents\_TEST_\*.sas*", repertoire_destination)  # modèle avec extension
# ou
lz.move(r"D:\OneDrive\Documents\_TEST_\*2020-11-11*", repertoire_destination)  # modèle sans extension, comprenant la chaîne "2020-11-11" dans le nom

# %%
# Déplacement groupé dans un dossier
lz.move((
    r"D:\OneDrive\Documents\_TEST_\*.sas*",  # avec schéma
    r"D:\OneDrive\Documents\_TEST_\2020-11-11.jpg",
    r"D:\OneDrive\Documents\_TEST_\__R2D2-LOGS__"  # dossier...
), repertoire_destination)

# %%
# Déplacement groupé d'une liste de fichiers dans une autre liste de fichiers (fonctionnement 1-1, i.e. même nombre de fichiers en entrée et en sortie, traitée de manière itérative)
lz.move((
    r"D:\OneDrive\Documents\_TEST_\*.sas*",  # avec schéma
    r"D:\OneDrive\Documents\_TEST_\2020-11-11.jpg",
    r"D:\OneDrive\Documents\_TEST_\__R2D2-LOGS__",  # dossier...
), (
    repertoire_destination,  # sans renommage
    repertoire_destination + "\\2020-11-11_NOUVEAU_NOM.jpg",  # avec renommage
    repertoire_destination + "\\__R2D2-NEW__",  # avec renommage
))


# %%
########################################################################################################################
# NOTES ################################################################################################################
# Comme sous Unix, move peut être utilisé pour renomer un fichier (chemin source = chemin cible)
########################################################################################################################
lz.move(r"D:\OneDrive\Documents\_TEST_\_éèçàoöôîïêëùûü;.txt", r"D:\OneDrive\Documents\_TEST_\NOUVEAU_NOM.txt")
corelibs.lazy.open_explorer(path)

Description

Permet d’ouvrir dans l’explorateur en pointant directement sur le chemin passé en argument.
Parameters

path – indique le chemin à pointer

Returns

rien…

Exemple :

# open_explorer.py
from corelibs import lazy as lz

lz.open_explorer(lz.get_home())
lz.open_explorer(r"D:\OneDrive\Documents\_TEST_")

lz.open_explorer(lz.get_home() + r"\.corelibs")  # ouvre le dossier contenant les données utilisateurs comme user_config.py

corelibs.lazy.rename(path, pattern, replace, transform=None, debug=True, verbose=False)

Description

Permet de renommer à la volée des fichiers selon des schémas regex.

Note

Cette fonction est un emballage de la fonction corelibs.lazy.move(). A l’utiliser de préférence si c’est un renommage simple.

Warning

Compte tenu des possibles dégâts liés aux erreurs de schéma regex, par défaut, la fonction s’exécute en mode debug = True
Pour que la fonction s’applique, il faut donc que le debug soit positionné à False explicitement.
Parameters
  • path – indique l’emplacement des fichiers sources à renommer en chemin absolu.

  • pattern

    indique le schéma regex des fichiers sources. Le schéma source doit contenir à minima des (), listant les différents sous groupes. Un séquençace est possible également via les directives suivantes :

    • %{0} pour un séquençage sans padding

    • %{n} où n est un entier indiquant le nombre de 0 en padding

  • replace – indique le schéma regex final de remplacement. Le schéma cible doit contenir à minima des \n où n est un entier représentant les sous groupes trouvés à partir du schéma source.

  • transform

    indique s’il y a des transformations à opérer ou non. Les transformations possibles sur le nom des fichiers cibles sont :

    • \U\n pour Uppercase sur le sous groupe identifié n

    • \L\n pour Lowercase sur le sous groupe identifié n

    • \C\n pour Capitalize (Premier mot en capitalizing) sur le sous groupe identifié n

    • \T\n pour Title (Premier Mot En Title) sur le sous groupe identifié n

    • \S\n pour Swapcase sur le sous groupe identifié n

  • debug

    indique si la fonction doit s’appliquer ou tourner à blanc

    valeurs possibles: False/True
    valeur par défaut: True

  • verbose

    afficher ou non les messages d’info/alertes. Dans le cadre d’une utilisation regex, le verbose peut se comporter comme un véritable spam…!!! =þ

    valeurs possibles: False/True
    valeur par défaut: DEFAULT_VERBOSE (cf. Installation & Mise à jour)

Returns

rien…

Exemple :

# rename.py
# %%
from corelibs import lazy as lz


# %%
# Renommage simple
lz.rename(
    path=r"D:\OneDrive\Documents\_TEST_\_TEST_RENOMMAGE_",
    pattern=r"_cars.sas7bdat",
    replace=r"nouveau_RS_cars.sas7bdat",
    debug=False
)  # équivalent à lz.move(r"D:\OneDrive\Documents\_TEST_\_TEST_RENOMMAGE_\_cars.sas7bdat", r"D:\OneDrive\Documents\_TEST_\_TEST_RENOMMAGE_\nouveau_RS_cars.sas7bdat")

# %%
# Renommage simple par modèle
lz.rename(
    r"D:\OneDrive\Documents\_TEST_\_TEST_RENOMMAGE_\__R2D2-LOGS__",
    r"(termcolorlog)_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})(.LOG)",  # Schéma source avec 8 sous groupes, (termcolorlog)_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})(.LOG)
    r"\1_\4\3\2_\5\6.log",  # Schéma cible décrivant "termcolorlog_JJMMAAAA_HHMM.log"
    debug=False
)
# résultats :
# termcolorlog_20201208_222147.LOG -> termcolorlog_08122020_2221.log
# termcolorlog_20201208_222351.LOG -> termcolorlog_08122020_2223.log
# termcolorlog_20201208_222452.LOG -> termcolorlog_08122020_2224.log
# termcolorlog_20201208_222839.LOG -> termcolorlog_08122020_2228.log
# termcolorlog_20201208_223739.LOG -> termcolorlog_08122020_2237.log
# termcolorlog_20201208_223834.LOG -> termcolorlog_08122020_2238.log
# termcolorlog_20201208_223920.LOG -> termcolorlog_08122020_2239.log
# termcolorlog_20201208_224058.LOG -> termcolorlog_08122020_2240.log
# termcolorlog_20201208_224116.LOG -> termcolorlog_08122020_2241.log
# termcolorlog_20201208_224144.LOG -> termcolorlog_08122020_2241.log

# %%
# Renommage simple par modèle avec transformation 1er exemple
lz.rename(
    r"D:\OneDrive\Documents\_TEST_\_TEST_RENOMMAGE_\__R2D2-LOGS__\__R2D2-LOGS__",
    r"(termcolorlog)_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})(.LOG)",  # Schéma source avec 8 sous groupes, (termcolorlog)_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})(.LOG)
    r"\1_\4\3\2_\5\6\8",  # Schéma cible décrivant "termcolorlog_JJMMAAAA_HHMM(.LOG)"
    transform=r"\U1\T8",  # Upper sur le premier groupe et Title sur le dernier groupe
    debug=False
)
# résultats :
# termcolorlog_20201208_222147.LOG -> TERMCOLORLOG_08122020_2221.Log
# termcolorlog_20201208_222351.LOG -> TERMCOLORLOG_08122020_2223.Log
# termcolorlog_20201208_222452.LOG -> TERMCOLORLOG_08122020_2224.Log
# termcolorlog_20201208_222839.LOG -> TERMCOLORLOG_08122020_2228.Log
# termcolorlog_20201208_223739.LOG -> TERMCOLORLOG_08122020_2237.Log
# termcolorlog_20201208_223834.LOG -> TERMCOLORLOG_08122020_2238.Log
# termcolorlog_20201208_223920.LOG -> TERMCOLORLOG_08122020_2239.Log
# termcolorlog_20201208_224058.LOG -> TERMCOLORLOG_08122020_2240.Log
# termcolorlog_20201208_224116.LOG -> TERMCOLORLOG_08122020_2241.Log
# termcolorlog_20201208_224144.LOG -> TERMCOLORLOG_08122020_2241.Log


# %%
# Renommage simple par modèle avec transformation 2ème exemple
lz.rename(
    r"D:\OneDrive\Documents\_TEST_\_TEST_RENOMMAGE_",
    r"_(cars)(.*)(.sas7bdat)",  # Schéma source avec 3 sous groupes
    r"NOUVEAU_NOM_\1_AVEC_TRANSFO\2.nouvelle_extension",  # Schéma cible décrivant "NOUVEAU_NOM_(cars)_AVEC_TRANSFO(.*).nouvelle_extension"
    transform=r"\S1\T2",  # Swapcase sur le premier groupe et Title sur le 2ème groupe
    debug=False
)
# résultats :
# _CaRS.sas7bdat -> NOUVEAU_NOM_cArs_AVEC_TRANSFO.nouvelle_extension
# _cars_asia.sas7bdat -> NOUVEAU_NOM_CARS_AVEC_TRANSFO_Asia.nouvelle_extension


# %%
# Renommage simple par modèle avec transformation et séquences sans padding 1er exemple
lz.rename(
    r"D:\OneDrive\Documents\_TEST_\_TEST_RENOMMAGE_\__R2D2__",
    r"(termcolorlog)_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})(.LOG)",  # Schéma source avec 8 sous groupes, (termcolorlog)_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})(.LOG)
    r"\1_\4\3\2_%{0}_\8",  # Schéma cible décrivant "TERMCOLORLOG_JJMMAAAA_%SEQUENCE_(.LOG)"
    transform=r"\U1\T8",  # Upper sur le premier groupe et Title sur le dernier groupe
    debug=False,
    verbose=True
)
# résultats :
# termcolorlog_20201208_222147.LOG -> TERMCOLORLOG_08122020_1_.Log
# termcolorlog_20201208_222351.LOG -> TERMCOLORLOG_08122020_2_.Log
# termcolorlog_20201208_222452.LOG -> TERMCOLORLOG_08122020_3_.Log
# termcolorlog_20201208_222839.LOG -> TERMCOLORLOG_08122020_4_.Log
# termcolorlog_20201208_223739.LOG -> TERMCOLORLOG_08122020_5_.Log
# termcolorlog_20201208_223834.LOG -> TERMCOLORLOG_08122020_6_.Log
# termcolorlog_20201208_223920.LOG -> TERMCOLORLOG_08122020_7_.Log
# termcolorlog_20201208_224058.LOG -> TERMCOLORLOG_08122020_8_.Log
# termcolorlog_20201208_224116.LOG -> TERMCOLORLOG_08122020_9_.Log
# termcolorlog_20201208_224144.LOG -> TERMCOLORLOG_08122020_10_.Log

# %%
# Renommage simple par modèle avec transformation et séquences avec padding 2ème exemple
lz.rename(
    r"D:\OneDrive\Documents\_TEST_\_TEST_RENOMMAGE_\__R2D2__\__R2D2-LOGS__",
    r"(termcolorlog)_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})(.LOG)",  # Schéma source avec 8 sous groupes, (termcolorlog)_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})(.LOG)
    r"_%{10}_\1_\4\3\2_%{0}_\8",  # Schéma cible décrivant "_%SEQUENCE_PADDING10_TERMCOLORLOG_JJMMAAAA_%SEQUENCE_PADDING_(.LOG)"
    transform=r"\U1\T8",  # Upper sur le premier groupe et Title sur le dernier groupe
    debug=False
)
# résultats :
# termcolorlog_20201208_182147.LOG -> _0000000001_TERMCOLORLOG_08122020_0000000001_.Log
# termcolorlog_20201208_182351.LOG -> _0000000002_TERMCOLORLOG_08122020_0000000002_.Log
# termcolorlog_20201208_182452.LOG -> _0000000003_TERMCOLORLOG_08122020_0000000003_.Log
# termcolorlog_20201208_182839.LOG -> _0000000004_TERMCOLORLOG_08122020_0000000004_.Log
# termcolorlog_20201208_183739.LOG -> _0000000005_TERMCOLORLOG_08122020_0000000005_.Log
# termcolorlog_20201208_183834.LOG -> _0000000006_TERMCOLORLOG_08122020_0000000006_.Log
# termcolorlog_20201208_183920.LOG -> _0000000007_TERMCOLORLOG_08122020_0000000007_.Log
# termcolorlog_20201208_184058.LOG -> _0000000008_TERMCOLORLOG_08122020_0000000008_.Log
# termcolorlog_20201208_184116.LOG -> _0000000009_TERMCOLORLOG_08122020_0000000009_.Log
# termcolorlog_20201208_184144.LOG -> _0000000010_TERMCOLORLOG_08122020_0000000010_.Log
corelibs.lazy.reverse_named_tuple(named_tuple, convert_2_dict=False, ignore_errors=False)

Description

Permet d’inverser l’ordre d’un tuple nommé.
Parameters
  • named_tuple – indique le tuple nommé à inverser

  • convert_2_dict

    indique s’il faut convertir ou non le tuple nommé en dictionnaire

    valeurs possibles: False/True
    valeur par défaut: False

  • ignore_errors

    forcer l’exécution lorsqu’une erreur est levée

    valeurs possibles: False/True
    valeur par défaut: False (cf. DEFAULT_IGNORE_ERROR dans Installation & Mise à jour)

Returns

tuple nommé avec le nom de classe et attributs tels que passés en argument
ou
dictionnaire (conversion du tuple nommé)

Exemple :

# reverse_named_tuple.py
# %%
from corelibs import lazy as lz


# %%
# création d'un tuple nommé
byte_size = lz.get_bytes_size_formats(1739886085)
print(byte_size)
# ByteSize(byte=1739886085, kilobyte=1699107.5, megabyte=1659.28, gigabyte=1.62, terabyte=0.0)

# %%
# renverser le tuple nommé
reverse_nt = lz.reverse_named_tuple(byte_size)
print(reverse_nt)
# ByteSize(terabyte=0.0, gigabyte=1.62, megabyte=1659.28, kilobyte=1699107.5, byte=1739886085.0)

# %%
# comme la classe originale existe lors de l'inversion, il est toujours possible d'accéder à un des attributs
print(reverse_nt.megabyte)  # affichera 1659.28

# %%
# renverser le tuple nommé avec conversion en dictionnaire
reverse_nt = lz.reverse_named_tuple(byte_size, convert_2_dict=True)
print(reverse_nt)
# {'terabyte': 0.0, 'gigabyte': 1.62, 'megabyte': 1659.28, 'kilobyte': 1699107.5, 'byte': 1739886085}

# %%
# exemple avec dir_n_basename()
dir_n_basename = lz.get_dir_n_basename(r"C:\Users\M47624\corelibs\tests\lazy\get_dir_n_basename.py")
print(dir_n_basename)
# DirPathnBaseName(dir_path='C:\\Users\\M47624\\corelibs\\tests\\lazy', base_name='get_dir_n_basename.py')

# %%
print(lz.reverse_named_tuple(dir_n_basename))
# DirPathnBaseName(base_name='get_dir_n_basename.py', dir_path='C:\\Users\\M47624\\corelibs\\tests\\lazy')

# %%
print(dir_n_basename.base_name)  # get_dir_n_basename.py

Module cleanse

Description générale

Module pour purifier/nettoyer les données

corelibs.cleanse.cleanse_file(file_path, strip_non_breaking_space=True, out_file_path=None, cleansed_suffix='_CLEANSED', time_stamp='DT', encoding='Latin-1', ignore_errors=False)

Description

Permet de purifier le fichier des caractères non imprimables, ainsi que les espaces insécables pouvant générer des erreurs lors des imports
Parameters
  • file_path – indique le fichier en entrée avec son chemin absolu

  • strip_non_breaking_space

    indique la suppression des espaces insécables

    valeurs possibles: False/True
    valeur par défaut: True

  • out_file_path – indique le fichier en sortie avec son chemin absolu

  • cleansed_suffix – indique le suffix à utiliser dans le nom de sortie (si out_file_path == None)

  • time_stamp

    indique le timestamp à appliquer dans le nom de sortie (si out_file_path == None)

    valeurs possibles: DT, D, T ou None
    valeur par défaut: DT

  • encoding – indique l’encodage à la lecture/écriture

  • ignore_errors

    forcer l’exécution lorsqu’une erreur est levée

    valeurs possibles: False/True
    valeur par défaut: False (cf. DEFAULT_IGNORE_ERROR dans Installation & Mise à jour)

Returns

rien…

Exemple :

# cleanse_file.py
# %%
from corelibs import cleanse as cls, log


# %%
# nettoyage fichier des caractères non imprimables
@log.timing()
@log.status_bar()
def stress_test():
    cls.cleanse_file(
        r"D:\OneDrive\Documents\_TEST_\StockEtablissement_utf8_A3T.csv",
        time_stamp="D",
        strip_non_breaking_space=True
    )


stress_test()
# non printable seul :
# Durée exécution : 00:01:36.11 pour 30 millions de lignes
# Durée exécution : 00:04:48.07 pour 90 millions de lignes
# non printable + non breaking space
# Durée exécution : 00:02:25.80 pour 30 millions de lignes
# Durée exécution : 00:07:11.64 pour 90 millions de lignes
corelibs.cleanse.is_datetime(dt_str, in_format='%d/%m/%Y %H:%M:%S', out_format='%d/%m/%Y %H:%M:%S', in_locale_time='fr', out_locale_time='fr', ignore_errors=False, check_only=False)

Description

Vérifie si une chaîne est un datetime ou non.
Permet également de convertir le datetime vers un format souhaité (cf. Liste des codes pour le formatage des timestamps pour les formats)
Parameters
  • dt_str – indique la variable horodatée (datetime ou string)

  • in_format – indique le format d’entrée (n’est pas nécessaire si le dt_str est une instance de datetime)

  • out_format – indique le format sortie

  • in_locale_time – indique la langue de la date en entrée (cf. DEFAULT_LOCALE_TIME dans Installation & Mise à jour). Si chaîne vide, alors la langue utilisée sera celle définie par le système d’exploitation sur lequel est exécutée corelibs.cleanse.is_datetime()

  • out_locale_time – indique la langue de la date en sortie (cf. DEFAULT_LOCALE_TIME dans Installation & Mise à jour). Si chaîne vide, alors la langue utilisée sera celle définie par le système d’exploitation sur lequel est exécutée corelibs.cleanse.is_datetime()

  • ignore_errors

    forcer l’exécution lorsqu’une erreur est levée

    valeurs possibles: False/True
    valeur par défaut: False (cf. DEFAULT_IGNORE_ERROR dans Installation & Mise à jour)

  • check_only

    permet de retourner un booléen au lieu de la chaîne de caractères transformée (utile dans le cas où il est seulement souhaité un contrôle, sans nécessairement une transformation pour éventuellement y appliquer une règle de gestion propre)

    valeurs possibles: False/True
    valeur par défaut: False

Returns

booléen: False/True si check_only == True (comportement par défaut)
ou
chaine vide (si forcé)
ou
None (si forcé lorsque dt_str est une instance de datetime)
ou
:magenta:(str)`datetime` au format défini via out_format

Exemple :

# is_datetime.py
# %%
import datetime as dt

from corelibs import cleanse as cls

# %%
# forcer le traitement des instructions suivantes même en erreur...
print(cls.is_datetime("Hello Kim", ignore_errors=True))  # retourne vide...

# %%
# Datetime en chaine de caractère
# forcer le retour à vide (utile pour les données loufoques de la morkitu)
print(cls.is_datetime("2020-08-31", "%Y-%m-%d", out_format=""))  # retourne vide...

# %%
print(cls.is_datetime("2020-08-31", "%Y-%m-%d"))  # retourne 31/08/2020 00:00:00
print(cls.is_datetime("lun., 31 août 2020 15:57:43", "%a, %d %b %Y %H:%M:%S"))  # 31/08/2020 15:57:43

# %%
# format entrée en anglais, avec une sortie française (par défaut)...
print(cls.is_datetime("Mon, 31 Aug 2020 15:57:43",
                      in_format="%a, %d %b %Y %H:%M:%S",
                      out_format="le %A %d %b %Y à %H h et %M min",
                      in_locale_time="en"))  # le lundi 31 août 2020 à 15 h et 57 min

# %%
# format entrée en anglais, avec une sortie suédoise...
print(cls.is_datetime("Mon, 30 Oct 2020 15:57:43",
                      in_format="%a, %d %b %Y %H:%M:%S",
                      out_format="%A %d %b %Y • %H:%M:%S",
                      in_locale_time="en",
                      out_locale_time="sv"))  # fredag 30 okt 2020 • 15:57:43

# %%
# format entrée en français (par défaut), avec une sortie anglaise...
print(cls.is_datetime("lun., 31 août 2020 15:57:43",
                      in_format="%a, %d %b %Y %H:%M:%S",
                      out_format="%A %d %b %Y @ %HH n %Mmin",
                      out_locale_time="en"))  # Monday 31 Aug 2020 @ 15H n 57min

# %%
# Instance datetime
_now = dt.datetime.now()
# sortie par défaut quand c'est une date...
print(cls.is_datetime(_now))  # retourne 15/12/2020 23:58:08

# %%
# sortie avec conversion format
print(cls.is_datetime(_now, out_format="le %A %d %b %Y à %H h et %M min"))  # le mardi 15 déc. 2020 à 23 h et 58 min
corelibs.cleanse.is_email(email, check_only=True, check_deliverability=False, normalize=True, correction=True, new_email='', extraction=False, smtp_utf8=False)

Description

Permet de valider et d’uniformiser un email
Parameters
  • email – email à traiter

  • check_only

    permet de retourner un booléen au lieu de l’email normalisé

    valeurs possibles: False/True
    valeur par défaut: True

  • check_deliverability

    indique s’il faut vérifier la délivrabilité de l’email (si son format est valide).

    valeurs possibles: False/True
    valeur par défaut: False

  • normalize

    indique s’il faut normaliser l’email en sortie

    valeurs possibles: False/True
    valeur par défaut: True

  • correction – indique s’il faut corriger l’email faut avec la valeur passée via new_email

  • new_email

    indique la chaîne de remplacement si l’email est faux

    valeurs possibles: chaine de caractères
    valeur par défaut: chaine vide

  • extraction

    indique s’il faut extraire l’email passé en argument

    valeurs possibles: False/True
    valeur par défaut: False

  • smtp_utf8

    indique si l’email passé en argument est encodé en utf8

    valeurs possibles: False/True
    valeur par défaut: False

Returns

booléen: False/True si check_only == True (comportement par défaut)
ou
email normalisé avec un encodage ASCII ou en UTF8 (si smtp_utf8 == True)
ou
tuple nommé avec comme attributs :
  • email (email complet normalisé en UTF8)

  • local_part (préfixe du @ en UTF8)

  • domain (suffixe du @ en UTF8)

  • ascii_email (email complet normalisé en ASCII)

  • ascii_local_part (préfixe du @ en ASCII ou None si smtp_utf8 == True)

  • ascii_domain (suffixe du @ en ASCII ou Punnycode si smtp_utf8 == True)

  • smtp_utf8

  • mx (entrée MX du/des serveurs DNS)

  • mx_fallback_type (None si tout est OK, sinon les possibles erreurs levées)

Exemple :

# is_email.py
# %%
from corelibs import cleanse as cls, log, config


config.DEFAULT_LOG_LEVEL = 10


@log.dict_dumping
def dump(tuple_2_dump):
    return tuple_2_dump


# vérification par défaut
print(cls.is_email("prenom.nom@BPIfrance.fr"))  # affichera True car correctement formaté

# vérification avec l'option déliverabilité avec une erreur typo dans le nom de domaine
print(cls.is_email("prenom.nom@bpi_france.fr", check_deliverability=True))  # affichera False car le nom de domaine bpi_france.fr n'existe pas

# vérification avec normalisation
print(cls.is_email("prenom.nom@BPIfrance.fr", check_only=False))  # affichera prenom.nom@bpifrance.fr

# vérification avec extraction des méta données
res = cls.is_email("prenom.nom@BPIfrance.fr", check_only=False, extraction=True, check_deliverability=True)
print(res)
# affichera l'objet
# Email(
#   email='prenom.nom@bpifrance.fr',
#   local_part='prenom.nom',
#   domain='bpifrance.fr',
#   ascii_email='prenom.nom@bpifrance.fr',
#   ascii_local_part='prenom.nom',
#   ascii_domain='bpifrance.fr',
#   smtp_utf8=False,
#   mx=[
#     (5, 'mail1.bpifrance.fr'),
#     (5, 'mail2.bpifrance.fr'),
#     (10, 'mail1.oseo.fr'),
#     (10, 'mail2.oseo.fr'),
#     (15, 'mail0.bpifrance.fr'),
#     (15, 'mail3.bpifrance.fr')],
#   mx_fallback_type=None
# )

dump(res)  # affichage plus lisible pour notre regard, au format YAML (mode DEBUG seulement)


# %%
# vérification avec un encodage utf8
print(cls.is_email("おはよう@例え.テスト", check_only=False, smtp_utf8=True))  # affichera おはよう@例え.テスト  <=> bonjour@parexemple.test

# extraction utf8
res = cls.is_email("おはよう@例え.テスト", check_only=False, extraction=True, smtp_utf8=True)
print(res)
# affichera l'objet
# Email(
#   email='おはよう@例え.テスト',
#   local_part='おはよう',
#   domain='例え.テスト',
#   ascii_email=None,
#   ascii_local_part=None,
#   ascii_domain='xn--r8jz45g.xn--zckzah',  # domaine ASCII encodé avec sa version Punycode (cf. https://www.rfc-editor.org/rfc/rfc3492.txt)
#   smtp_utf8=True,
#   mx=None,
#   mx_fallback_type=None
# )

dump(res)  # affichage plus lisible pour notre regard, au format YAML (mode DEBUG seulement)
_images/is_email.png
corelibs.cleanse.is_phone_number(phone_number, check_only=True, normalize=True, country_code='FR', phone_number_format=1, correction=True, new_phone='', extraction=False)

Description

Permet de qualifier un n° de téléphone au format international, national ou E164
Parameters
  • phone_number – n° de téléphone à traiter

  • check_only

    permet de retourner un booléen au lieu du n° de téléphone normalisé

    valeurs possibles: False/True
    valeur par défaut: True

  • normalize

    indique s’il faut normaliser le n° de téléphone

    valeurs possibles: False/True
    valeur par défaut: True

  • country_code

    indique le code du pays (code ISO sur 2 caractères)

    valeurs possibles: Code ISO pays ou None (si None alors le n° de téléphone doit être au format international, +XX??????????)
    valeur par défaut: FR

  • phone_number_format

    indique le format de sortie

    valeurs possibles: config.PHONE_NUMBER_FORMAT_INTERNATIONAL, config.PHONE_NUMBER_FORMAT_NATIONAL ou config.PHONE_NUMBER_FORMAT_E164
    valeur par défaut: config.PHONE_NUMBER_FORMAT_INTERNATIONAL

  • correction

    indique s’il faut nettoyer/consolider le n° de téléphone

    valeurs possibles: False/True
    valeur par défaut: True

  • new_phone

    indique la chaîne de remplacement si le n° de téléphone est faux

    valeurs possibles: chaine de caractères
    valeur par défaut: chaine vide

  • extraction

    indique s’il faut extraire le n° passé en argument via phone_number

    valeurs possibles: False/True
    valeur par défaut: False

Returns

booléen: False/True si check_only == True (comportement par défaut)
ou
n° de téléphone normalisé au format phone_number_format et consolidé si correction == True
ou
tuple nommé avec comme attributs :
  • country_code

  • phone_number

  • national_number (présent ici seulement par commodité)

  • international_number (présent ici seulement par commodité)

  • e164_number (présent ici seulement par commodité)

  • given_number

Exemple :

# is_phone_number.py
# %%
from corelibs import cleanse as cls, log, config

config.DEFAULT_LOG_LEVEL = 10


# %%
# qualification par défaut, en n° français
liste_nationale = (
    "06 89 97.21-31",  # OK
    "06    89.97.21-32",  # OK
    "01.89.97.21.32",  # OK
    "01.89.97.21.320",  # KO
)

res = []
for n in liste_nationale:
    res.append(cls.is_phone_number(n))
print(res)  # affichera [True, True, True, False]


# %%
# qualifacation avec des n° au format international
liste_internationale = (
    "+3306 89 97.21-31",  # français OK
    "+3306 89 97.21-31 0",  # français KO
    "+4412 89.97.21-3------------------2",  # anglais OK
    "+4412 89.97.21-3------------------2 0",  # anglais KO
    "+1510 - 748 - 8230",  # américain OK
    "+1510 - 748 - 8230 - 0",  # américain KO
)
res = []
for n in liste_internationale:
    res.append(cls.is_phone_number(n))
print(res)  # affichera [True, False, True, False, True, False]


# %%
# normalisation et correction des n° de téléphone
res = []
for n in liste_internationale:
    res.append(cls.is_phone_number(n, check_only=False))
print(res)  # affichera ['+33 6 89 97 21 31', '', '+44 1289 972132', '', '+1 510-748-8230', '']


# %%
# extraction des n° de téléphone
def _():
    res = []
    for phone_number in liste_internationale:
        res.append(cls.is_phone_number(phone_number, check_only=False, extraction=True))

    return res


phones_number = _()
print(phones_number)  # affichera
# [
#   PhoneNumber(country_code=33, phone_number=689972131, national_number='06 89 97 21 31', international_number='+33 6 89 97 21 31', e164_number='+33689972131', given_number='+3306 89 97.21-31'),
#   PhoneNumber(country_code='', phone_number='', national_number='', international_number='', e164_number='', given_number='+3306 89 97.21-31 0'),
#   PhoneNumber(country_code=44, phone_number=1289972132, national_number='01289 972132', international_number='+44 1289 972132', e164_number='+441289972132', given_number='+4412 89.97.21-3------------------2'),
#   PhoneNumber(country_code='', phone_number='', national_number='', international_number='', e164_number='', given_number='+4412 89.97.21-3------------------2 0'),
#   PhoneNumber(country_code=1, phone_number=5107488230, national_number='(510) 748-8230', international_number='+1 510-748-8230', e164_number='+15107488230', given_number='+1510 - 748 - 8230'),
#   PhoneNumber(country_code='', phone_number='', national_number='', international_number='', e164_number='', given_number='+1510 - 748 - 8230 - 0')
# ]


# %%
# dumping pour une meilleure visualisation
@log.dict_dumping
def dump(tuple_2_dump):
    return tuple_2_dump


for phone in phones_number:
    dump(phone)  # affichage plus lisible pour notre regard, au format YAML (mode DEBUG seulement)
_images/is_phone_number.png
corelibs.cleanse.is_schema(string, schema, regex_flag=re.IGNORECASE)

Description

Permet de qualifier une chaîne de caractères par rapport à un schéma regex
Parameters
  • string – chaine de caractères à qualifier

  • schema – schéma regex

  • regex_flag

    indique le flag regex à utiliser. cf. Liens utiles, Librairie regex pour plus de détails

    valeurs possibles: None ou constante regex (séparée par | si plusieurs constantes, par exemple re.M | re.I)
    valeur par défaut: re.IGNORECASE

Returns

booléen: False/True

Exemple :

# is_schema.py
# %%
from corelibs import cleanse as cls


chaine = """
Somewhere, something incredible is waiting to be known.

For small creatures such as we the vastness is bearable only through love.

We're made of star stuff. We are a way for the cosmos to know itself.

If you want to make an apple pie from scratch, you must first create the universe.

Science is a way of thinking much more than it is a body of knowledge.

Imagination will often carry us to worlds that never were. But without it we go nowhere.

Every one of us is, in the cosmic perspective, precious. If a human disagrees with you, let him live. In a hundred billion galaxies, you will not find another.

Science is not only compatible with spirituality; it is a profound source of spirituality.

Absence of evidence is not evidence of absence.

We live in a society exquisitely dependent on science and technology, in which hardly anyone knows anything about science and technology.

===

Quelque part, quelque chose d'incroyable attend d'être connu.

Pour les petites créatures comme nous, l'immensité n'est supportable que par l'amour.

Nous sommes faits de poussières d'étoiles. Nous sommes un moyen pour le cosmos de se connaître.

Si vous voulez faire une tarte aux pommes à partir de rien, vous devez d'abord créer l'univers.

La science est une façon de penser bien plus qu’un ensemble de connaissances.

L'imagination nous transportera souvent dans des mondes qui ne l'ont jamais été. Mais sans elle, nous allons nulle part.

Chacun de nous est, dans la perspective cosmique, précieux. Si une personne n'est pas d'accord avec vous, laissez la vivre. Dans un rayon de cent milliards de galaxies, vous n'en trouverez pas d'autres.

La science n'est pas seulement compatible avec la spiritualité; c'est une source profonde de spiritualité.

L'absence de preuve n'est pas une preuve d'absence.

Nous vivons dans une société extrêmement dépendante de la science et de la technologie, dans laquelle presque personne ne sait rien de la science et de la technologie.

Carl SAGAN
"""

# recherche début de chaîne...
print(cls.is_schema(chaine, schema=r"^somewhere.*", regex_flag=None))  # affichera False car Somewhere ne commence qu'à la 2ème ligne

# recherche du mot somewhere sans distinction
print(cls.is_schema(chaine, schema=r"somewhere"))  # affichera True

# recherche début de chaîne en "cassant" la chaîne principale
for s in chaine.split("\n"):
    if cls.is_schema(s, schema=r"^somewhere.*"):
        print(s)  # affichera Somewhere, something incredible is waiting to be known.

# recherche si signé par Carl SAGAN
print(cls.is_schema(chaine, schema=r"Carl SAGAN$"))  # affichera True


# %%
# Exemple plus concrète et sérieuse =þ
# validation d'un format de fichier avec ID, Prénom et Date anniversaire sous la forme JJ/MM/AAAA, séparé par des ;
# ce format est traduit par le schéma suivant ^[0-9]{2};\w+;([0-9]{2}\/){2}[0-9]{4}
chaine = """
ID;Prénom;Date Anniversaire
01;Kim;28/11/2013
02,Mickey,18/11/1928
XX;YODA;1980
"""

_chaine = chaine.split("\n")
res = _chaine[0:2]

for s in _chaine[2:-1]:
    if cls.is_schema(s, r"^[0-9]{2};\w+;([0-9]{2}\/){2}[0-9]{4}"):
        res.append(s + "OK".rjust(31 - len(s)))
    else:
        res.append(s + "KO".rjust(31 - len(s)))

res.append("".join(_chaine[-1:]))  # inutile mais pour être rigoureux, il y a la dernière chaine à blanc qui doit être prise en compte...
print("\n".join(res))
# ce qui affichera, avec la 2ème ligne validée et les autres lignes non validées
# ID;Prénom;Date Anniversaire
# 01;Kim;28/11/2013            OK
# 02,Mickey,18/11/1928         KO
# XX;YODA;1980                 KO
corelibs.cleanse.is_siren(siren, check_only=True, correction=True, new_siren='')

Description

Vérifie si un siren est correctement formaté et est valide au sens technique du terme (cf. alogrithme de Luhn)
Parameters
  • siren – chaine de caractères ou liste de chaînes de caractères à traiter

  • check_only

    permet de retourner un booléen au lieu du siren corrigé (si en erreur) ou consolidé (si il manque des caractères 0 devant pour former un siren à 9 caractères)

    valeurs possibles: False/True
    valeur par défaut: True

  • correction

    indique s’il faut nettoyer/consolider le siren

    valeurs possibles: False/True
    valeur par défaut: True

  • new_siren

    indique la chaîne de remplacement si le siren est faux

    valeurs possibles: chaine de caractères
    valeur par défaut: chaine vide

Returns

booléen: False/True si check_only == True (comportement par défaut)
ou
siren/liste de siren corrigé/consolidé (si correction == True)

Exemple :

# is_siren.py
# %%
from corelibs import cleanse as cls

# vérification sur 1 siren correctement formaté
res = cls.is_siren("325175")
print(res)  # affichera un booléen, ici True

# vérication sur le même siren avec un retour en chaîne de cractères
res = cls.is_siren("325175", check_only=False)
print(res)  # affichera "000325175" qui est la version consolidée, avec le bon formatage pour respecter le format du siren sur 9 caractères

# vérification sur une liste de sirens
sirens = (
    "732829320",  # siren sur 9 OK
    "303663981",  # siren sur 9 KO
    "60801487",  # siren sur 8 OK
    "5450093",  # siren sur 7 OK
    "325175",  # siren sur 6 OK
)

print(cls.is_siren(sirens))  # affichera [True, False, True, True, True]
print(cls.is_siren(sirens, check_only=False))  # affichera la version corrigée ['732829320', '', '060801487', '005450093', '000325175'] <= le 2ème siren qui est faux est par défaut remplacé par une chaîne vide
print(cls.is_siren(sirens, check_only=False, new_siren="999999999"))  # affichera ['732829320', '999999999', '060801487', '005450093', '000325175'], au lieu de ['732829320', '', '060801487', '005450093', '000325175'] qui est le comportement par défaut
corelibs.cleanse.is_siret(siret, check_only=True, correction=True, new_siret='', extraction=False)

Description

Vérifie si un siret est correctement formaté et est valide au sens technique du terme (cf. alogrithme de Luhn) - prend également en compte les établissements particuliers de La Poste ayant pour sirène 356000000
Parameters
  • siret – chaine de caractères ou liste de chaînes de caractères à traiter

  • check_only

    permet de retourner un booléen au lieu du siret corrigé (si en erreur) ou consolidé (si il manque des caractères 0 devant pour former un siret à 15 caractères)

    valeurs possibles: False/True
    valeur par défaut: True

  • correction

    indique s’il faut nettoyer/consolider le siret

    valeurs possibles: False/True
    valeur par défaut: True

  • new_siret

    indique la chaîne de remplacement si le siret est faux

    valeurs possibles: chaine de caractères
    valeur par défaut: chaine vide

  • extraction

    indique s’il faut extraire le siren/nic du siret

    valeurs possibles: False/True
    valeur par défaut: False

Returns

booléen: False/True si check_only == True (comportement par défaut)
ou
siret/liste de siret corrigé/consolidé (si correction == True)
ou
tuple nommé avec comme attributs :
  • siren

  • nic

  • siret (présent ici seulement par commodité)

Exemple :

# is_siret.py
# %%
from corelibs import cleanse as cls

# vérification sur 1 siret correctement formaté
res = cls.is_siret(("33243395200022"))
print(res)  # affichera un booléen, ici True

# vérication sur le même siret avec un retour en chaîne de cractères
res = cls.is_siret("33243395200022", check_only=False)
print(res)  # affichera 33243395200022

# extraction du même siret validé
res = cls.is_siret("33243395200022", check_only=False, extraction=True)
print(res)  # affichera Siret(siren='332433952', nic='00022', siret='33243395200022')
print(f"Le siret {res.siret} est composé du siren {res.siren} et du nic {res.nic}")  # affichera Le siret 33243395200022 est composé du siren 332433952 et du nic 00022

# vérification sur une liste de sirets
sirets = (
    "33243395200022",  # siret sur 14 OK
    "33243395200021",  # siret sur 14 KO
    "00032517500057",  # siret sur 14 OK
    "32517500024",  # siret sur 11 OK
    "32517501024",  # siret sur 11 KO
    "35600000049837",  # cas particulier La Poste
    "35600000052135",  # cas particulier La Poste
)

print(cls.is_siret(sirets))  # affichera [True, False, True, True, False, True, True]
print(cls.is_siret(sirets, check_only=False))  # affichera la version corrigée ['33243395200022', '', '00032517500057', '00032517500024', '', '35600000049837', '35600000052135']
print(cls.is_siret(sirets, check_only=False, new_siret="99999999999999"))  # affichera ['33243395200022', '99999999999999', '00032517500057', '00032517500024', '99999999999999', '35600000049837', '35600000052135']


# extractions sur la même liste avec une annotation quand le siren est celui de La Poste
is_laposte = lambda _: "(Établissement La Poste)" if _ == "356000000" else ""
for s in cls.is_siret(sirets, check_only=False, new_siret="---------------", extraction=True):
    print(f"Le siret {s.siret} est composé du siren {s.siren} et du nic {s.nic} {is_laposte(s.siren)}")
# affichera
# Le siret 33243395200022 est composé du siren 332433952 et du nic 00022
# Le siret --------------- est composé du siren --------- et du nic -----
# Le siret 00032517500057 est composé du siren 000325175 et du nic 00057
# Le siret 00032517500024 est composé du siren 000325175 et du nic 00024
# Le siret --------------- est composé du siren --------- et du nic -----
# Le siret 35600000049837 est composé du siren 356000000 et du nic 49837 (Établissement La Poste)
# Le siret 35600000052135 est composé du siren 356000000 et du nic 52135 (Établissement La Poste)
corelibs.cleanse.is_str(string, strip_non_printable_char=True, strip_accented_char=False, strip_num_char=False, chars_2_replace=None, replaced_chars=None, unicode_categories_2_remove=None, check_only=False)

Description

Vérifie si une chaîne de caractères est bien une chaine de caractères.
Permet également de faire du nettoyage de caractères
Parameters
  • string – chaine de caractères à traiter

  • strip_non_printable_char

    permet de nettoyer la chaine de tous les caractères de contrôles parasites.

    valeurs possibles: False/True
    valeur par défaut: True

  • strip_accented_char

    permet de nettoyer la chaine de tous les caractères accentués, avec leurs équivaleuts sans accent.

    valeurs possibles: False/True
    valeur par défaut: False

  • strip_num_char

    permet de nettoyer la chaine de tous les caractères numériques parasites (avec ou non des caractères séparateurs, ainsi que les signes et/ou opérateur comme le “-” ou “:”)

    valeurs possibles: False/True
    valeur par défaut: False

  • chars_2_replace

    permet de faire du remplacement de caractères unitairement.

    valeurs possibles: Chaine de caractère ou Dictionnaire associatif (si Dictionnaire, alors l’argument replaced_chars est optionnel, cf. exemple)

  • replaced_chars – indique la liste des caractères de remplacement, si cet argument est renseigné, doit fonctionner conjointement avec l’argument chars_2_replace (cf. exemple)

  • unicode_categories_2_remove

    permet de spécifier une classe de caractères Unicode à supprimer. Cet argument est optionnel et doit être utilisé avec l’argument strip_non_printable_char

    valeurs possibles: Liste ou Tuple de classe Unicode (cf. Classification des caractères Unicodes pour plus de détails sur les classifications des caractères Unicodes)
    valeur par défaut: None

  • check_only

    permet de retourner un booléen au lieu de la chaîne de caractères transformée (utile dans le cas où il est seulement souhaité un contrôle, sans nécessairement une transformation pour éventuellement y appliquer une règle de gestion propre)

    valeurs possibles: False/True
    valeurs par défaut: False

Returns

chaine de caractères transformée si check_only == False (comportement par défaut)
sinon True/False

Note

si chars_2_replace est un dictionnaire alors il est possible de remplacer plusieurs caractères d’un seul tenant {“ABC”: “Nouvelle chaines de caractères”} - ou la suite “ABC” sera remplacée par “Nouvelle chaines de caractères” (cf. exemple ci-dessous)

Exemple :

# is_str.py
# %%
from corelibs import cleanse as cls


# test si chaine caractères
print(cls.is_str(123))  # affichera une chaîne vide ""

# suppression de tous les caractères non imprimables
# \x00 <=> NULL (NUL)
# \x01 <=> START OF HEADING (SOH)
# \x02 <=> START OF TEXT (STX)
# \x03 <=> END OF TEXT (ETX)
# \x04 <=> END OF TRANSMISSION (EOT)
# \x05 <=> END OF QUERY (ENQ)
# \x06 <=> ACKNOWLEDGE (ACK)
# \x07 <=> BEEP (BEL)
# \x08 <=> BACKSPACE (BS)
# \x09 <=> HORIZONTAL TAB (HT)
# \x0A <=> LINE FEED (LF)
# \x0B <=> VERTICAL TAB (VT)
# \x0C <=> FF (FORM FEED)
# \x0D <=> CR (CARRIAGE RETURN)
# \x0E <=> SO (SHIFT OUT)
# \x0F <=> SI (SHIFT IN)
# \x10 <=> DATA LINK ESCAPE (DLE)
# \x11 <=> DEVICE CONTROL 1 (DC1)
# \x12 <=> DEVICE CONTROL 2 (DC2)
# \x13 <=> DEVICE CONTROL 3 (DC3)
# \x14 <=> DEVICE CONTROL 4 (DC4)
# \x15 <=> NEGATIVE ACKNOWLEDGEMENT (NAK)
# \x16 <=> SYNCHRONIZE (SYN)
# \x17 <=> END OF TRANSMISSION BLOCK (ETB)
# \x18 <=> CANCEL (CAN)
# \x19 <=> END OF MEDIUM (EM)
# \x1A <=> SUBSTITUTE (SUB)
# \x1B <=> ESCAPE (ESC)
# \x1C <=> FILE SEPARATOR (FS) RIGHT ARROW
# \x1D <=> GROUP SEPARATOR (GS) LEFT ARROW
# \x1E <=> RECORD SEPARATOR (RS) UP ARROW
# \x1F <=> UNIT SEPARATOR (US) DOWN ARROW
chaine = """
\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19
\x1A\x1B\x1C\x1D\x1E\x1F Hello, je suis la chaîne qui ne sera pas nettoyée... \x12\x13\x14\x15\x16\x17\x18
❤ =}
"""
print(cls.is_str(chaine))  # affiche " Hello, je suis la chaîne qui ne sera pas nettoyée...  ❤ =}"

# %%
# Supression via des classes de caractères Unicode
# Cc => Caractères de contrôles (non imprimables)
# Lu => Caractères majuscules
# Zs => espace
print(cls.is_str(chaine, unicode_categories_2_remove=("Cc", "Lu", "Zs"))) # affiche "ello,jesuislachaînequineserapasnettoyée...❤=}"

# %%
# suppression des caractères accentués en les ramplaçant par leurs équivalents non accentués
chaine = "Hello, voici ma liste de caractères accentués ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿŒœŠšŸƒ"
print(cls.is_str(chaine, strip_accented_char=True))  # affiche "Hello, voici ma liste de caracteres accentues AAAAAAÆCEEEEIIIIÐNOOOOOØUUUUYÞßaaaaaaæceeeeiiiiðnoooooøuuuuyþyŒœSsYƒ"

# %%
# suppression des caractères numériques avec les signes, opérateurs et séparateurs
chaine = "Hello, voici ma liste avec des caractères numériques : date(28/11/2013) heure(19:31) normal(123456) " \
         "séparateur (012.2345.12) signe + 12.2345.12 separateur 12,123,456 - 123 separateur 10 123 456 " \
         "signe - 123.12 signe et sep -12,123 signe et sep -10 012,123 1456"
print(cls.is_str(chaine, strip_num_char=True))  # affiche "Hello, voici ma liste avec des caractères numériques : date() heure() normal() séparateur () signe separateur separateur signe signe et sep signe et sep"

# %%
# remplacement des caractères, K -> A, i -> n et m -> T 3 -> 7
# 1er exemple avec une liste de caractères
chaine = "Hello, ma \x12\x13\x14\x15\x16\x17\x18 chaine avec des caractères à remplacer : Kim Marie Adélie, ma petite choupinette a 3 ans (casse prise en coMpte...)"
print(cls.is_str(chaine, chars_2_replace="Kim3", replaced_chars="AnT7"))  # affiche "Hello, Ta  channe avec des caractères à reTplacer : AnT Marne Adélne, Ta petnte choupnnette a 7 ans (casse prnse en coMpte...)"

# %%
# 2ème exemple avec un dictionnaire
print(cls.is_str(chaine,
                 chars_2_replace={
                     "Kim Marie Adélie": "Kim Marie Adélie TRUONG",  # multi char
                     "3": "7",  # single char
                     "M": "m"
                 }))  # Hello, ma  chaine avec des caractères à remplacer : Kim Marie Adélie TRUONG, ma petite choupinette a 7 ans (casse prise en compte...)
corelibs.cleanse.replace(string, search, replace, regex_flag=None, encoding='Latin-1')

Description

Permet de remplacer la valeur d’une chaine de caractères avec un modèle regex ou une expression simple
Parameters
  • string – chaine de caractères à traiter

  • search – indique la chaine de caractères ou le modèle regex à chercher.

  • replace – indique la chaine de caractères ou le modèle regex en remplacement.

  • regex_flag – indique les flags à utiliser. cf. Liens utiles, Librairie regex pour plus de détails

  • encoding – indique l’encodage à la lecture/écriture

Returns

chaine de caractères transformée

Exemple :

# replace.py
# %%
import re

from corelibs import config, cleanse as cls

# %%
# remplacement simple
print(
    cls.replace(
        "Coucou Kim, c'est papa ❤ =}",
        "Kim",
        "Kim Marie Adélie"
    )
)  # affiche Coucou Kim Marie Adélie, c'est papa ❤ =}

# remplacement simple (binaire, encodage latin)
print(
    cls.replace(
        "Coucou Kim, c'est papa =}",  # le coeur ne peut pas être utilisé ici car non présent dans l'encodage latin-1... sinon son code est 0xE2 0x99 0xA5
        b"Kim",
        b"Kim Marie Ad\xE9lie",
        encoding=config.DEFAULT_ENCODING_FILE  # "ISO-8859-1"
    )
)  # affiche Coucou Kim Marie Adélie, c'est papa =}

# remplacement simple (binaire, encodage utf-8)
print(
    cls.replace(
        "Coucou Kim, c'est papa ❤ =}",
        b"Kim",
        b"Kim Marie Ad\xC3\xA9lie",
        encoding="utf-8"
    )
)  # affiche Coucou Kim Marie Adélie, c'est papa ❤ =}


# %%
# remplacement avec regex (changement format date, AAAA-MM-DD deviendra DD/MM/AAAA)
string = """
999990583;00133;99999058300133;O;1993-01-01;NN;;;2006-11-25T22:25:28;false;4;CENTRALE DE MIJANES;;;;;09120;VARILHES;;;09324;;;;;;;;;;;;;;;;;;;2004-06-30;F;;;;;40.1A;NAFRev1;N
999990583;00158;99999058300158;O;1998-09-07;NN;;;2019-11-14T14:00:51;true;3;PARC TECHNOL LA PARDIEU;6;;RUE;CONDORCET;63000;CLERMONT-FERRAND;;;63113;;;;;;;;;;;;;;;;;;;2004-06-30;F;;;;;28.4A;NAFRev1;N
999990609;00011;99999060900011;O;;NN;;;;true;1;;4;;PL;DE LA PYRAMIDE;92800;PUTEAUX;;;92062;;;;;;;;;;;;;;;;;;;1991-12-30;F;;;;;73.1Z;NAF1993;N
999990625;00025;99999062500025;O;;NN;;;;true;1;;;;RTE;DE MANOM;57100;THIONVILLE;;;57672;;;;;;;;;;;;;;;;;;;1995-11-30;F;;;;;27.3C;NAF1993;O
999990641;00014;99999064100014;O;;NN;;;;true;1;;99;;BD;DE GRENELLE;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;1986-06-15;F;;;;;65.01;NAP;N
999990666;00011;99999066600011;O;1986-05-15;;;;;false;4;;10;;RUE;CHAUCHAT;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;1997-12-03;F;;;;;66.0A;NAF1993;N
999990666;00029;99999066600029;O;1997-12-03;;;;;false;3;;2;;RUE;PILLET WILL;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;2000-07-01;F;;;;;66.0A;NAF1993;N
999990666;00037;99999066600037;O;2000-07-01;NN;;;2020-05-20T03:34:55;true;3;8 A 10;8;;RUE;D'ASTORG;75008;PARIS 8;;;75108;;;;;;;;;;;;;;;;;;;2008-01-01;A;;;;;65.11Z;NAFRev2;N
999990682;00034;99999068200034;O;2001-09-18;NN;;;;true;3;LE PONANT DE PARIS;27;;RUE;LEBLANC;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;2003-12-18;F;;;;;65.2E;NAFRev1;N
999992357;00015;99999235700015;O;2003-12-31;01;2017;;2019-06-24T14:13:19;true;5;;6;;RUE;DE L ETOILE;80090;AMIENS;;;80021;;;;;;;;;;;;;;;;;;;2012-01-22;A;;;;;81.10Z;NAFRev2;O
"""
print(
    cls.replace(
        string,
        search=r"\b(\d{4})-(\d{2})-(\d{2})\b",
        replace=r"\3/\2/\1"
    )
)
# affiche (les champs dates sont bien modifiés comme souhaité, pas les champs horodatés comme AAAA-MM-DDTHH:MM)
# 999990583;00133;99999058300133;O;01/01/1993;NN;;;2006-11-25T22:25:28;false;4;CENTRALE DE MIJANES;;;;;09120;VARILHES;;;09324;;;;;;;;;;;;;;;;;;;30/06/2004;F;;;;;40.1A;NAFRev1;N
# 999990583;00158;99999058300158;O;07/09/1998;NN;;;2019-11-14T14:00:51;true;3;PARC TECHNOL LA PARDIEU;6;;RUE;CONDORCET;63000;CLERMONT-FERRAND;;;63113;;;;;;;;;;;;;;;;;;;30/06/2004;F;;;;;28.4A;NAFRev1;N
# 999990609;00011;99999060900011;O;;NN;;;;true;1;;4;;PL;DE LA PYRAMIDE;92800;PUTEAUX;;;92062;;;;;;;;;;;;;;;;;;;30/12/1991;F;;;;;73.1Z;NAF1993;N
# 999990625;00025;99999062500025;O;;NN;;;;true;1;;;;RTE;DE MANOM;57100;THIONVILLE;;;57672;;;;;;;;;;;;;;;;;;;30/11/1995;F;;;;;27.3C;NAF1993;O
# 999990641;00014;99999064100014;O;;NN;;;;true;1;;99;;BD;DE GRENELLE;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;15/06/1986;F;;;;;65.01;NAP;N
# 999990666;00011;99999066600011;O;15/05/1986;;;;;false;4;;10;;RUE;CHAUCHAT;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;03/12/1997;F;;;;;66.0A;NAF1993;N
# 999990666;00029;99999066600029;O;03/12/1997;;;;;false;3;;2;;RUE;PILLET WILL;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;01/07/2000;F;;;;;66.0A;NAF1993;N
# 999990666;00037;99999066600037;O;01/07/2000;NN;;;2020-05-20T03:34:55;true;3;8 A 10;8;;RUE;D'ASTORG;75008;PARIS 8;;;75108;;;;;;;;;;;;;;;;;;;01/01/2008;A;;;;;65.11Z;NAFRev2;N
# 999990682;00034;99999068200034;O;18/09/2001;NN;;;;true;3;LE PONANT DE PARIS;27;;RUE;LEBLANC;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;18/12/2003;F;;;;;65.2E;NAFRev1;N
# 999992357;00015;99999235700015;O;31/12/2003;01;2017;;2019-06-24T14:13:19;true;5;;6;;RUE;DE L ETOILE;80090;AMIENS;;;80021;;;;;;;;;;;;;;;;;;;22/01/2012;A;;;;;81.10Z;NAFRev2;O


# %%
# remplacement avec regex (changement format date + format horodaté, AAAA-MM-DD deviendra DD/MM/AAAA et AAAA-MM-DDTHH:MM deviendra DD/MM/AAAA HH:MM)
print(
    cls.replace(
        cls.replace(
            string,
            search=r"\b(\d{4})-(\d{2})-(\d{2})\b",
            replace=r"\3/\2/\1"
        ),
        search=r"\b(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\b",
        replace=r"\3/\2/\1 \4:\5"
    )
)
# affiche (les champs dates et champs horodatés comme 2006-11-25T22 sont bien modifiés comme souhaité)
# 999990583;00133;99999058300133;O;01/01/1993;NN;;;25/11/2006 22:25;false;4;CENTRALE DE MIJANES;;;;;09120;VARILHES;;;09324;;;;;;;;;;;;;;;;;;;30/06/2004;F;;;;;40.1A;NAFRev1;N
# 999990583;00158;99999058300158;O;07/09/1998;NN;;;14/11/2019 14:00;true;3;PARC TECHNOL LA PARDIEU;6;;RUE;CONDORCET;63000;CLERMONT-FERRAND;;;63113;;;;;;;;;;;;;;;;;;;30/06/2004;F;;;;;28.4A;NAFRev1;N
# 999990609;00011;99999060900011;O;;NN;;;;true;1;;4;;PL;DE LA PYRAMIDE;92800;PUTEAUX;;;92062;;;;;;;;;;;;;;;;;;;30/12/1991;F;;;;;73.1Z;NAF1993;N
# 999990625;00025;99999062500025;O;;NN;;;;true;1;;;;RTE;DE MANOM;57100;THIONVILLE;;;57672;;;;;;;;;;;;;;;;;;;30/11/1995;F;;;;;27.3C;NAF1993;O
# 999990641;00014;99999064100014;O;;NN;;;;true;1;;99;;BD;DE GRENELLE;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;15/06/1986;F;;;;;65.01;NAP;N
# 999990666;00011;99999066600011;O;15/05/1986;;;;;false;4;;10;;RUE;CHAUCHAT;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;03/12/1997;F;;;;;66.0A;NAF1993;N
# 999990666;00029;99999066600029;O;03/12/1997;;;;;false;3;;2;;RUE;PILLET WILL;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;01/07/2000;F;;;;;66.0A;NAF1993;N
# 999990666;00037;99999066600037;O;01/07/2000;NN;;;20/05/2020 03:34;true;3;8 A 10;8;;RUE;D'ASTORG;75008;PARIS 8;;;75108;;;;;;;;;;;;;;;;;;;01/01/2008;A;;;;;65.11Z;NAFRev2;N
# 999990682;00034;99999068200034;O;18/09/2001;NN;;;;true;3;LE PONANT DE PARIS;27;;RUE;LEBLANC;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;18/12/2003;F;;;;;65.2E;NAFRev1;N
# 999992357;00015;99999235700015;O;31/12/2003;01;2017;;24/06/2019 14:13;true;5;;6;;RUE;DE L ETOILE;80090;AMIENS;;;80021;;;;;;;;;;;;;;;;;;;22/01/2012;A;;;;;81.10Z;NAFRev2;O



# %%
# remplacement avec regex & utilisation flags
string = """
Tuesday 08 December 2020 - Ma log à moi que j'ai [termcolorlog.py:1] <>          INFO : Bonjour, ceci est un test personnalisé niveau INFO
Tuesday 08 December 2020 - Ma log à moi que j'ai [termcolorlog.py:1] <>       WARNING : Bonjour, ceci est un test personnalisé niveau WARNING
Tuesday 08 December 2020 - Ma log à moi que j'ai [termcolorlog.py:1] <>         ERROR : Bonjour, ceci est un test personnalisé niveau ERROR
Tuesday 08 December 2020 - Ma log à moi que j'ai [termcolorlog.py:1] <>      CRITICAL : Bonjour, ceci est un test personnalisé niveau CRITIQUE
"""
print(
    cls.replace(
        string,
        search=r"^(tuesday) (08) (december) (2020)(.*)(Bonjour,)(.*)$",  # justification de l'utilisation du flag re.I
        replace=r"\4/12/\2\5Au revoir!\7 - modifié par regex =}",
        regex_flag=re.M | re.I
    )
)
# affiche
# 2020/12/08 - Ma log à moi que j'ai [termcolorlog.py:1] <>          INFO : Au revoir! ceci est un test personnalisé niveau INFO - modifié par regex =}
# 2020/12/08 - Ma log à moi que j'ai [termcolorlog.py:1] <>       WARNING : Au revoir! ceci est un test personnalisé niveau WARNING - modifié par regex =}
# 2020/12/08 - Ma log à moi que j'ai [termcolorlog.py:1] <>         ERROR : Au revoir! ceci est un test personnalisé niveau ERROR - modifié par regex =}
# 2020/12/08 - Ma log à moi que j'ai [termcolorlog.py:1] <>      CRITICAL : Au revoir! ceci est un test personnalisé niveau CRITIQUE - modifié par regex =}


# %%
# remplacement binaire des espace par des ;
print(
    cls.replace(
        string,
        search=b"\x20",
        replace=b"\x3B"
    )
)
# affiche
# Tuesday;08;December;2020;-;Ma;log;à;moi;que;j'ai;[termcolorlog.py:1];<>;;;;;;;;;;INFO;:;Bonjour,;ceci;est;un;test;personnalisé;niveau;INFO
# Tuesday;08;December;2020;-;Ma;log;à;moi;que;j'ai;[termcolorlog.py:1];<>;;;;;;;WARNING;:;Bonjour,;ceci;est;un;test;personnalisé;niveau;WARNING
# Tuesday;08;December;2020;-;Ma;log;à;moi;que;j'ai;[termcolorlog.py:1];<>;;;;;;;;;ERROR;:;Bonjour,;ceci;est;un;test;personnalisé;niveau;ERROR
# Tuesday;08;December;2020;-;Ma;log;à;moi;que;j'ai;[termcolorlog.py:1];<>;;;;;;CRITICAL;:;Bonjour,;ceci;est;un;test;personnalisé;niveau;CRITIQUE
corelibs.cleanse.replace_chaining(string, **kwargs)

Description

Wrapper de la fonction corelibs.cleanse.replace() permettant de chaîner cette dernière, de manière à faciliter la lisibilité et optimiser les traitements de données
Parameters
  • string – chaine de caractères à traiter

  • kwargs – liste dynamique d’arguments nommés. Cette liste accepte toutes les données loufoques. Cependant seules les fonctions corelibs.cleanse.replace() et corelibs.cleanse.is_str() seront traitées (cf. Exemple pour plus de précisions)

Returns

chaine de caractères transformée

Exemples :

# replace_chaining.py
# %%
from corelibs import cleanse as cls, lazy as lz


# %%
# enchainement (plus lisible)
string = """
999990583;00133;99999058300133;O;1993-01-01;NN;;;2006-11-25T22:25:28;false;4;CENTRALE DE MIJANES;;;;;09120;VARILHES;;;09324;;;;;;;;;;;;;;;;;;;2004-06-30;F;;;;;40.1A;NAFRev1;N
999990583;00158;99999058300158;O;1998-09-07;NN;;;2019-11-14T14:00:51;true;3;PARC TECHNOL LA PARDIEU;6;;RUE;CONDORCET;63000;CLERMONT-FERRAND;;;63113;;;;;;;;;;;;;;;;;;;2004-06-30;F;;;;;28.4A;NAFRev1;N
999990609;00011;99999060900011;O;;NN;;;;true;1;;4;;PL;DE LA PYRAMIDE;92800;PUTEAUX;;;92062;;;;;;;;;;;;;;;;;;;1991-12-30;F;;;;;73.1Z;NAF1993;N
999990625;00025;99999062500025;O;;NN;;;;true;1;;;;RTE;DE MANOM;57100;THIONVILLE;;;57672;;;;;;;;;;;;;;;;;;;1995-11-30;F;;;;;27.3C;NAF1993;O
999990641;00014;99999064100014;O;;NN;;;;true;1;;99;;BD;DE GRENELLE;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;1986-06-15;F;;;;;65.01;NAP;N
999990666;00011;99999066600011;O;1986-05-15;;;;;false;4;;10;;RUE;CHAUCHAT;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;1997-12-03;F;;;;;66.0A;NAF1993;N
999990666;00029;99999066600029;O;1997-12-03;;;;;false;3;;2;;RUE;PILLET WILL;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;2000-07-01;F;;;;;66.0A;NAF1993;N
999990666;00037;99999066600037;O;2000-07-01;NN;;;2020-05-20T03:34:55;true;3;8 A 10;8;;RUE;D'ASTORG;75008;PARIS 8;;;75108;;;;;;;;;;;;;;;;;;;2008-01-01;A;;;;;65.11Z;NAFRev2;N
999990682;00034;99999068200034;O;2001-09-18;NN;;;;true;3;LE PONANT DE PARIS;27;;RUE;LEBLANC;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;2003-12-18;F;;;;;65.2E;NAFRev1;N
999992357;00015;99999235700015;O;2003-12-31;01;2017;;2019-06-24T14:13:19;true;5;;6;;RUE;DE L ETOILE;80090;AMIENS;;;80021;;;;;;;;;;;;;;;;;;;2012-01-22;A;;;;;81.10Z;NAFRev2;O
"""
res = cls.replace_chaining(
    string,
    udf_1=lambda x: cls.replace(x, r"\b(\d{4})-(\d{2})-(\d{2})\b", r"\3/\2/\1"),  # transformation AAAA-MM-DD en DD/MM/AAAA
    udf_3=lambda x: cls.replace(x, r"\b(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\b", r"\3/\2/\1 \4:\5"),  # transformation AAAA-MM-DDTHH:MM en DD/MM/AAAA HH:MM
    arg_dynamique="Coucou",  # ne sera pas traité car n'est pas un callable
    function_dynamique=lz.get_home,  # callable mais ne sera pas non plus traité car n'est pas un replace()
    switch_separator=lambda x: cls.replace(x, b"\x3B", b"\x7C")  # remplacer les ; en | comme caractère séparateur
    # liste infinie dynamique des fonctions appelables à la suite... (si utile et pertinent)
)
print(res)
# affiche
# 999990583|00133|99999058300133|O|01/01/1993|NN|||25/11/2006 22:25|false|4|CENTRALE DE MIJANES|||||09120|VARILHES|||09324|||||||||||||||||||30/06/2004|F|||||40.1A|NAFRev1|N
# 999990583|00158|99999058300158|O|07/09/1998|NN|||14/11/2019 14:00|true|3|PARC TECHNOL LA PARDIEU|6||RUE|CONDORCET|63000|CLERMONT-FERRAND|||63113|||||||||||||||||||30/06/2004|F|||||28.4A|NAFRev1|N
# 999990609|00011|99999060900011|O||NN||||true|1||4||PL|DE LA PYRAMIDE|92800|PUTEAUX|||92062|||||||||||||||||||30/12/1991|F|||||73.1Z|NAF1993|N
# 999990625|00025|99999062500025|O||NN||||true|1||||RTE|DE MANOM|57100|THIONVILLE|||57672|||||||||||||||||||30/11/1995|F|||||27.3C|NAF1993|O
# 999990641|00014|99999064100014|O||NN||||true|1||99||BD|DE GRENELLE|75015|PARIS 15|||75115|||||||||||||||||||15/06/1986|F|||||65.01|NAP|N
# 999990666|00011|99999066600011|O|15/05/1986|||||false|4||10||RUE|CHAUCHAT|75009|PARIS 9|||75109|||||||||||||||||||03/12/1997|F|||||66.0A|NAF1993|N
# 999990666|00029|99999066600029|O|03/12/1997|||||false|3||2||RUE|PILLET WILL|75009|PARIS 9|||75109|||||||||||||||||||01/07/2000|F|||||66.0A|NAF1993|N
# 999990666|00037|99999066600037|O|01/07/2000|NN|||20/05/2020 03:34|true|3|8 A 10|8||RUE|D'ASTORG|75008|PARIS 8|||75108|||||||||||||||||||01/01/2008|A|||||65.11Z|NAFRev2|N
# 999990682|00034|99999068200034|O|18/09/2001|NN||||true|3|LE PONANT DE PARIS|27||RUE|LEBLANC|75015|PARIS 15|||75115|||||||||||||||||||18/12/2003|F|||||65.2E|NAFRev1|N
# 999992357|00015|99999235700015|O|31/12/2003|01|2017||24/06/2019 14:13|true|5||6||RUE|DE L ETOILE|80090|AMIENS|||80021|||||||||||||||||||22/01/2012|A|||||81.10Z|NAFRev2|O



# %%
# sinon en version non enchainée, 1ère possibilité (imbrication)
print(
    cls.replace(
        cls.replace(
            cls.replace(
                string,
                search=r"\b(\d{4})-(\d{2})-(\d{2})\b",
                replace=r"\3/\2/\1"
            ),
            search=r"\b(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\b",
            replace=r"\3/\2/\1 \4:\5"
        ),
        b"\x3B",
        b"\x7C"
    )
)
# affiche bien l'attendu
# 999990583|00133|99999058300133|O|01/01/1993|NN|||25/11/2006 22:25|false|4|CENTRALE DE MIJANES|||||09120|VARILHES|||09324|||||||||||||||||||30/06/2004|F|||||40.1A|NAFRev1|N
# 999990583|00158|99999058300158|O|07/09/1998|NN|||14/11/2019 14:00|true|3|PARC TECHNOL LA PARDIEU|6||RUE|CONDORCET|63000|CLERMONT-FERRAND|||63113|||||||||||||||||||30/06/2004|F|||||28.4A|NAFRev1|N
# 999990609|00011|99999060900011|O||NN||||true|1||4||PL|DE LA PYRAMIDE|92800|PUTEAUX|||92062|||||||||||||||||||30/12/1991|F|||||73.1Z|NAF1993|N
# 999990625|00025|99999062500025|O||NN||||true|1||||RTE|DE MANOM|57100|THIONVILLE|||57672|||||||||||||||||||30/11/1995|F|||||27.3C|NAF1993|O
# 999990641|00014|99999064100014|O||NN||||true|1||99||BD|DE GRENELLE|75015|PARIS 15|||75115|||||||||||||||||||15/06/1986|F|||||65.01|NAP|N
# 999990666|00011|99999066600011|O|15/05/1986|||||false|4||10||RUE|CHAUCHAT|75009|PARIS 9|||75109|||||||||||||||||||03/12/1997|F|||||66.0A|NAF1993|N
# 999990666|00029|99999066600029|O|03/12/1997|||||false|3||2||RUE|PILLET WILL|75009|PARIS 9|||75109|||||||||||||||||||01/07/2000|F|||||66.0A|NAF1993|N
# 999990666|00037|99999066600037|O|01/07/2000|NN|||20/05/2020 03:34|true|3|8 A 10|8||RUE|D'ASTORG|75008|PARIS 8|||75108|||||||||||||||||||01/01/2008|A|||||65.11Z|NAFRev2|N
# 999990682|00034|99999068200034|O|18/09/2001|NN||||true|3|LE PONANT DE PARIS|27||RUE|LEBLANC|75015|PARIS 15|||75115|||||||||||||||||||18/12/2003|F|||||65.2E|NAFRev1|N
# 999992357|00015|99999235700015|O|31/12/2003|01|2017||24/06/2019 14:13|true|5||6||RUE|DE L ETOILE|80090|AMIENS|||80021|||||||||||||||||||22/01/2012|A|||||81.10Z|NAFRev2|O


# %%
# sinon en version non enchainée, 2ème possibilité (avec étapes intermédiaires)
etape_1 = cls.replace(
    string,
    search=r"\b(\d{4})-(\d{2})-(\d{2})\b",
    replace=r"\3/\2/\1"
)

etape_2 = cls.replace(
    etape_1,
    search=r"\b(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\b",
    replace=r"\3/\2/\1 \4:\5"
)

print(
    cls.replace(
        etape_2,
        search=b"\x3B",
        replace=b"\x7C"
    )
)
# affiche bien l'attendu
# 999990583|00133|99999058300133|O|01/01/1993|NN|||25/11/2006 22:25|false|4|CENTRALE DE MIJANES|||||09120|VARILHES|||09324|||||||||||||||||||30/06/2004|F|||||40.1A|NAFRev1|N
# 999990583|00158|99999058300158|O|07/09/1998|NN|||14/11/2019 14:00|true|3|PARC TECHNOL LA PARDIEU|6||RUE|CONDORCET|63000|CLERMONT-FERRAND|||63113|||||||||||||||||||30/06/2004|F|||||28.4A|NAFRev1|N
# 999990609|00011|99999060900011|O||NN||||true|1||4||PL|DE LA PYRAMIDE|92800|PUTEAUX|||92062|||||||||||||||||||30/12/1991|F|||||73.1Z|NAF1993|N
# 999990625|00025|99999062500025|O||NN||||true|1||||RTE|DE MANOM|57100|THIONVILLE|||57672|||||||||||||||||||30/11/1995|F|||||27.3C|NAF1993|O
# 999990641|00014|99999064100014|O||NN||||true|1||99||BD|DE GRENELLE|75015|PARIS 15|||75115|||||||||||||||||||15/06/1986|F|||||65.01|NAP|N
# 999990666|00011|99999066600011|O|15/05/1986|||||false|4||10||RUE|CHAUCHAT|75009|PARIS 9|||75109|||||||||||||||||||03/12/1997|F|||||66.0A|NAF1993|N
# 999990666|00029|99999066600029|O|03/12/1997|||||false|3||2||RUE|PILLET WILL|75009|PARIS 9|||75109|||||||||||||||||||01/07/2000|F|||||66.0A|NAF1993|N
# 999990666|00037|99999066600037|O|01/07/2000|NN|||20/05/2020 03:34|true|3|8 A 10|8||RUE|D'ASTORG|75008|PARIS 8|||75108|||||||||||||||||||01/01/2008|A|||||65.11Z|NAFRev2|N
# 999990682|00034|99999068200034|O|18/09/2001|NN||||true|3|LE PONANT DE PARIS|27||RUE|LEBLANC|75015|PARIS 15|||75115|||||||||||||||||||18/12/2003|F|||||65.2E|NAFRev1|N
# 999992357|00015|99999235700015|O|31/12/2003|01|2017||24/06/2019 14:13|true|5||6||RUE|DE L ETOILE|80090|AMIENS|||80021|||||||||||||||||||22/01/2012|A|||||81.10Z|NAFRev2|O
# replace_chaining_AL.py
# exemple programme pour gérer le mailing list d'Alex L.
from corelibs import cleanse as cls

# %%
with open(r"D:\OneDrive\Desktop\Alex_L_LD.txt") as f_in:
    string = f_in.read()


def name_2_upper(m):
    return m.groups()[1] + " " + m.groups()[0].upper()[:-1] + " " + m.groups()[2]


def mail_2_name(m):
    return m.groups()[0].title() + " " + m.groups()[1].upper() + \
           " <" + m.groups()[0].lower() + "." + m.groups()[1].lower() + m.groups()[2].lower() + ">"


res = cls.replace_chaining(
    string,
    udf_0=lambda x: cls.is_str(x, chars_2_replace={  # nettoyage caractères zarbioïdes
        "é": "é",
        "ô": "ô",
        "ç": "ç",
        "Ç": "C"
    }),
    udf_1=lambda x: cls.replace(x, r"((([-\w',çéô Ç]* )*(<[-\w.@]*>)|([\w .@]*)))(;)", r"\1\n"),  # séparer en liste
    udf_2=lambda x: cls.replace(x, r"(^[ ]+)?(.*)", r"\2"),  # suppression démarrage par espace(s)
    udf_3=lambda x: cls.replace(x, r"'(.*),? (.*)' (.*)", name_2_upper),  # gestion modèle sous forme "Prénoms Composés NOMS COMPOSÉS <email>"
    udf_4=lambda x: cls.replace(x, r"^(?!(?:DOCUMENTATION).*$)(\w*).(\w*)(@[\w.]*)", mail_2_name),  # gestion modèle sous forme "prenom.nom@bpifrance.fr" sans prendre en compte le cas particulier "DOCUMENTATION documentation@bpifrance.fr"
    udf_5=lambda x: cls.replace(x, r"(([A-Z][-,a-zéèôç. ']+[ ]*)+) (([-A-Z ]+) ?)+ <(.*)>", r"\1;\3;\5"),  # formatage final Prénom Composé;NOMS COMPOSÉS;email@domain
    udf_6=lambda x: cls.replace(x, r"^(?!(.*;){2}(.*))([\w\- ]+) <?([\w.@]*)>?", r"\3;;\4;VALIDATION MANUELLE"),  # tout le reste, à surveiller manuellement car les formatages n'ont pas été identifiés par les schémas ci-dessus...
)

# affichage sortie standard
print(res)


# ou sortie vers fichier
with open(r"D:\OneDrive\Desktop\Alex_L_LD_FORMATED.txt", "w") as f_out:
    f_out.write(res)

Exemple Appel depuis R :

# treticulate_clease.r
# exemple appel des fonctions du package corelibs depuis R...

# install.packages("reticulate")  # si n'existe pas...
# install.packages("readr")

library("reticulate")
library(readr)

reticulate::use_condaenv(condaenv="test_conda", required=TRUE)
cleanse <- reticulate::import("corelibs.cleanse", delay_load=TRUE)

file_2_cleanse <- read_file(file="D:\\OneDrive\\Desktop\\Alex_L_LD.txt")

data <- cleanse$is_str(file_2_cleanse, chars_2_replace="é", replaced_chars="é")
data <- cleanse$is_str(file_2_cleanse, chars_2_replace="ô", replaced_chars="ô")
data <- cleanse$is_str(file_2_cleanse, chars_2_replace="ç", replaced_chars="ç")
data <- cleanse$is_str(file_2_cleanse, chars_2_replace="Ç", replaced_chars="C")

data <- cleanse$replace(
  data,
  search="((([-\\w',çéô Ç]* )*(<[-\\w.@]*>)|([\\w .@]*)))(;)",
  replace="\\1\\n"
)

data <- cleanse$replace(data, "(^[ ]+)?(.*)", "\\2")

f <- py_run_string("
def name_2_upper(m):
    return m.groups()[1] + \" \" + m.groups()[0].upper()[:-1] + \" \" + m.groups()[2]
")
data <- cleanse$replace(
  data,
  "'(.*),? (.*)' (.*)",
  f$name_2_upper
)

f <- py_run_string("
def mail_2_name(m):
    return m.groups()[0].title() + \" \" + m.groups()[1].upper() + \" <\" + m.groups()[0].lower() + \".\" + m.groups()[1].lower() + m.groups()[2].lower() + \">\"
")
data <- cleanse$replace(
  data,
  "^(?!(?:DOCUMENTATION).*$)(\\w*).(\\w*)(@[\\w.]*)",
  f$mail_2_name
)

data <- cleanse$replace(
  data,
  "(([A-Z][-,a-zéèôç. ']+[ ]*)+) (([-A-Z ]+) ?)+ <(.*)>",
  "\\1;\\3;\\5"
)

data <- cleanse$replace(
  data,
  "^(?!(.*;){2}(.*))([\\w\\- ]+) <?([\\w.@]*)>?",
  "\\3;;\\4;VALIDATION MANUELLE"
)

f_out <- file("D:\\OneDrive\\Desktop\\Alex_L_LD_FORMATED_BY_R.txt")
writeLines(data, f_out)
close(f_out)
corelibs.cleanse.strip(string, search=None, replace='', non_printable_char=True, non_breaking_space=True, multi_space=True, empty_line=True, accented_char=False, num_char=False, unicode_categories=None, regex_flag=None, encoding='Latin-1')

Description

Permet d’enlever des caractères ou chaines de caractères avec un modèle ou expression

Note

strip() a le même comportement que corelibs.cleanse.replace() si replace prend une valeur différente de vide. l’intérêt de strip() est d’avoir des modèles prédéfinis pour purifier les fichiers et/ou des chaînes de caractères avec des caractères loufoques
Parameters
  • string – chaine de caractères à traiter

  • search – indique la chaine de caractères ou le modèle regex à chercher (si les autres options ne répondent pas aux besoins).

  • replace – indique le caractère ou la chaîne de caractères de remplacement (rien par défaut)

  • non_printable_char

    indique la suppression de tous les caracètres non imprimables

    valeurs possibles: False/True
    valeur par défaut: True

  • non_breaking_space

    indique la suppression de tous les espaces insécables (non breaking space)

    valeurs possibles: False/True
    valeur par défaut: True

  • multi_space

    indique la suppression des multiple espaces contigüs par un seul espace

    valeurs possibles: False/True
    valeur par défaut: True

  • empty_line

    indique la suppression des lignes vides

    valeurs possibles: False/True
    valeur par défaut: True

  • accented_char

    indique la suppression des caractères accentués

    valeurs possibles: False/True
    valeur par défaut: False

  • num_char

    indique la suppression des caractères contenant des valeurs numériques (formatées ou non)

    valeurs possibles: False/True
    valeur par défaut: False

  • unicode_categories

    permet de spécifier une classe de caractères Unicode à supprimer.

    valeurs possibles: Liste ou Tuple de classe Unicode (cf. Classification des caractères Unicodes pour plus de détails sur les classifications des caractères Unicodes)

  • regex_flag – indique les flags à utiliser. cf. Liens utiles, Librairie regex pour plus de détails

  • encoding – indique l’encodage à la lecture/écriture

Returns

chaine de caractères transformée

Exemple :

# strip.py
# %%
from corelibs import cleanse as cls

# %%
# suppression de tous les caractères non imprimables
# \x00 <=> NULL (NUL)
# \x01 <=> START OF HEADING (SOH)
# \x02 <=> START OF TEXT (STX)
# \x03 <=> END OF TEXT (ETX)
# \x04 <=> END OF TRANSMISSION (EOT)
# \x05 <=> END OF QUERY (ENQ)
# \x06 <=> ACKNOWLEDGE (ACK)
# \x07 <=> BEEP (BEL)
# \x08 <=> BACKSPACE (BS)
# \x09 <=> HORIZONTAL TAB (HT)
# \x0A <=> LINE FEED (LF)
# \x0B <=> VERTICAL TAB (VT)
# \x0C <=> FORM FEED (FF)
# \x0D <=> CARRIAGE RETURN (CR)
# \x0E <=> SHIFT OUT (SO)
# \x0F <=> SHIFT IN (SI)
# \x10 <=> DATA LINK ESCAPE (DLE)
# \x11 <=> DEVICE CONTROL 1 (DC1)
# \x12 <=> DEVICE CONTROL 2 (DC2)
# \x13 <=> DEVICE CONTROL 3 (DC3)
# \x14 <=> DEVICE CONTROL 4 (DC4)
# \x15 <=> NEGATIVE ACKNOWLEDGEMENT (NAK)
# \x16 <=> SYNCHRONIZE (SYN)
# \x17 <=> END OF TRANSMISSION BLOCK (ETB)
# \x18 <=> CANCEL (CAN)
# \x19 <=> END OF MEDIUM (EM)
# \x1A <=> SUBSTITUTE (SUB)
# \x1B <=> ESCAPE (ESC)
# \x1C <=> FILE SEPARATOR (FS) RIGHT ARROW
# \x1D <=> GROUP SEPARATOR (GS) LEFT ARROW
# \x1E <=> RECORD SEPARATOR (RS) UP ARROW
# \x1F <=> UNIT SEPARATOR (US) DOWN ARROW
string = """
\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19
\x1A\x1B\x1C\x1D\x1E\x1F Hello, je suis la chaîne qui va rester après nettoyage... \x12\x13\x14\x15\x16\x17\x18
❤ =}
"""
print(cls.strip(string))
# affiche " Hello, je suis la chaîne qui va rester après nettoyage... ❤ =}"


# %%
# supression de tous les caractères non imprimables, avec un remplacement différent
print(cls.strip(string, replace="-"))
# affiche "-----------------------------------Hello,-je-suis-la-chaîne-qui-va-rester-après-nettoyage...---------❤-=}-"


# %%
# Supression via des classes de caractères Unicode
# Cc => Caractères de contrôles (non imprimables)
# Lu => Caractères majuscules
# Zs => espace
print(cls.strip(string, unicode_categories=("Cc", "Lu", "Zs")))
# affiche "ello,jesuislachaînequivaresteraprèsnettoyage...❤=}"


# %%
# Supression multiple espaces (comportement par défaut)
string = """
\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19
o( ^   ^ o)     Hello Kim !!!     (o ^   ^ )o   
\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19
"""
print(cls.strip(string))
# affiche "o( ^ ^ o) Hello Kim !!! (o ^ ^ )o"

# %%
# Supression espace insécable
string = "Cette chaine contient 3 espaces -(\xA0\xA0\xA0)- insécables"
print(string)  # affiche "Cette chaine contient 3 espaces -(   )- insécables"
print(cls.strip(string))  # affiche "Cette chaine contient 3 espaces -()- insécables"


# %%
# suppression des caractères accentués en les ramplaçant par leurs équivalents non accentués
string = "Hello, voici ma liste de caractères accentués ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿŒœŠšŸƒ"
print(cls.strip(string, accented_char=True))
# affiche "Hello, voici ma liste de caracteres accentues AAAAAAÆCEEEEIIIIÐNOOOOOØUUUUYÞßaaaaaaæceeeeiiiiðnoooooøuuuuyþyŒœSsYƒ"


# %%
# suppression des caractères numériques avec les signes, opérateurs et séparateurs
string = """
Hello, voici ma liste avec des caractères numériques : 
date -(28/11/2013)- 
heure -(19:31)- 
normal -(123456)- 
signe -(+ 12.2345.12)- ou -(- 12.2345.12)- 
séparateur -(012.2345.12)- ou -(12,123,456)- ou -( 10 123 456)- 
signe et sep -(-10 012,123 1456)- 
"""
print(cls.strip(string, num_char=True, non_printable_char=False))
# affiche "
# Hello, voici ma liste avec des caractères numériques :
# date -()-
# heure -()-
# normal -()-
# signe -()- ou -()-
# séparateur -()- ou -()- ou -()-
# signe et sep -()-
# "


# %%
# suppression par recherche standard (on enlève ici tout ce qui est entre les ; - juste pour le fun car aucun intérêt...)
string = """
999990583;00133;99999058300133;O;1993-01-01;NN;;;2006-11-25T22:25:28;false;4;CENTRALE DE MIJANES;;;;;09120;VARILHES;;;09324;;;;;;;;;;;;;;;;;;;2004-06-30;F;;;;;40.1A;NAFRev1;N
999990583;00158;99999058300158;O;1998-09-07;NN;;;2019-11-14T14:00:51;true;3;PARC TECHNOL LA PARDIEU;6;;RUE;CONDORCET;63000;CLERMONT-FERRAND;;;63113;;;;;;;;;;;;;;;;;;;2004-06-30;F;;;;;28.4A;NAFRev1;N
999990609;00011;99999060900011;O;;NN;;;;true;1;;4;;PL;DE LA PYRAMIDE;92800;PUTEAUX;;;92062;;;;;;;;;;;;;;;;;;;1991-12-30;F;;;;;73.1Z;NAF1993;N
999990625;00025;99999062500025;O;;NN;;;;true;1;;;;RTE;DE MANOM;57100;THIONVILLE;;;57672;;;;;;;;;;;;;;;;;;;1995-11-30;F;;;;;27.3C;NAF1993;O
999990641;00014;99999064100014;O;;NN;;;;true;1;;99;;BD;DE GRENELLE;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;1986-06-15;F;;;;;65.01;NAP;N
999990666;00011;99999066600011;O;1986-05-15;;;;;false;4;;10;;RUE;CHAUCHAT;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;1997-12-03;F;;;;;66.0A;NAF1993;N
999990666;00029;99999066600029;O;1997-12-03;;;;;false;3;;2;;RUE;PILLET WILL;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;2000-07-01;F;;;;;66.0A;NAF1993;N
999990666;00037;99999066600037;O;2000-07-01;NN;;;2020-05-20T03:34:55;true;3;8 A 10;8;;RUE;D'ASTORG;75008;PARIS 8;;;75108;;;;;;;;;;;;;;;;;;;2008-01-01;A;;;;;65.11Z;NAFRev2;N
999990682;00034;99999068200034;O;2001-09-18;NN;;;;true;3;LE PONANT DE PARIS;27;;RUE;LEBLANC;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;2003-12-18;F;;;;;65.2E;NAFRev1;N
999992357;00015;99999235700015;O;2003-12-31;01;2017;;2019-06-24T14:13:19;true;5;;6;;RUE;DE L ETOILE;80090;AMIENS;;;80021;;;;;;;;;;;;;;;;;;;2012-01-22;A;;;;;81.10Z;NAFRev2;O
"""
print(cls.strip(string, search=r"[^;\n]+", non_printable_char=False))
# affiche "
# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
# "


# %%
# suppression des ;  avec remplacement par , (en binaire)
print(cls.strip(string, search=b"\x3B", non_printable_char=False, replace=b"\x2C"))
# affiche "
# 999990583,00133,99999058300133,O,1993-01-01,NN,,,2006-11-25T22:25:28,false,4,CENTRALE,DE,MIJANES,,,,,09120,VARILHES,,,09324,,,,,,,,,,,,,,,,,,,2004-06-30,F,,,,,40.1A,NAFRev1,N
# 999990583,00158,99999058300158,O,1998-09-07,NN,,,2019-11-14T14:00:51,true,3,PARC,TECHNOL,LA,PARDIEU,6,,RUE,CONDORCET,63000,CLERMONT-FERRAND,,,63113,,,,,,,,,,,,,,,,,,,2004-06-30,F,,,,,28.4A,NAFRev1,N
# 999990609,00011,99999060900011,O,,NN,,,,true,1,,4,,PL,DE,LA,PYRAMIDE,92800,PUTEAUX,,,92062,,,,,,,,,,,,,,,,,,,1991-12-30,F,,,,,73.1Z,NAF1993,N
# 999990625,00025,99999062500025,O,,NN,,,,true,1,,,,RTE,DE,MANOM,57100,THIONVILLE,,,57672,,,,,,,,,,,,,,,,,,,1995-11-30,F,,,,,27.3C,NAF1993,O
# 999990641,00014,99999064100014,O,,NN,,,,true,1,,99,,BD,DE,GRENELLE,75015,PARIS,15,,,75115,,,,,,,,,,,,,,,,,,,1986-06-15,F,,,,,65.01,NAP,N
# 999990666,00011,99999066600011,O,1986-05-15,,,,,false,4,,10,,RUE,CHAUCHAT,75009,PARIS,9,,,75109,,,,,,,,,,,,,,,,,,,1997-12-03,F,,,,,66.0A,NAF1993,N
# 999990666,00029,99999066600029,O,1997-12-03,,,,,false,3,,2,,RUE,PILLET,WILL,75009,PARIS,9,,,75109,,,,,,,,,,,,,,,,,,,2000-07-01,F,,,,,66.0A,NAF1993,N
# 999990666,00037,99999066600037,O,2000-07-01,NN,,,2020-05-20T03:34:55,true,3,8,A,10,8,,RUE,D'ASTORG,75008,PARIS,8,,,75108,,,,,,,,,,,,,,,,,,,2008-01-01,A,,,,,65.11Z,NAFRev2,N
# 999990682,00034,99999068200034,O,2001-09-18,NN,,,,true,3,LE,PONANT,DE,PARIS,27,,RUE,LEBLANC,75015,PARIS,15,,,75115,,,,,,,,,,,,,,,,,,,2003-12-18,F,,,,,65.2E,NAFRev1,N
# 999992357,00015,99999235700015,O,2003-12-31,01,2017,,2019-06-24T14:13:19,true,5,,6,,RUE,DE,L,ETOILE,80090,AMIENS,,,80021,,,,,,,,,,,,,,,,,,,2012-01-22,A,,,,,81.10Z,NAFRev2,O
# "


# %%
# suppression lignes vides
string = """

999990609,00011,99999060900011,O,,NN,,,,true,1,,4,,PL,DE,LA,PYRAMIDE,92800,PUTEAUX,,,92062,,,,,,,,,,,,,,,,,,,1991-12-30,F,,,,,73.1Z,NAF1993,N
999990625,00025,99999062500025,O,,NN,,,,true,1,,,,RTE,DE,MANOM,57100,THIONVILLE,,,57672,,,,,,,,,,,,,,,,,,,1995-11-30,F,,,,,27.3C,NAF1993,O

999990666,00011,99999066600011,O,1986-05-15,,,,,false,4,,10,,RUE,CHAUCHAT,75009,PARIS,9,,,75109,,,,,,,,,,,,,,,,,,,1997-12-03,F,,,,,66.0A,NAF1993,N
999990666,00029,99999066600029,O,1997-12-03,,,,,false,3,,2,,RUE,PILLET,WILL,75009,PARIS,9,,,75109,,,,,,,,,,,,,,,,,,,2000-07-01,F,,,,,66.0A,NAF1993,N



"""
print(cls.strip(string, non_printable_char=False))
# affiche
# 999990609,00011,99999060900011,O,,NN,,,,true,1,,4,,PL,DE,LA,PYRAMIDE,92800,PUTEAUX,,,92062,,,,,,,,,,,,,,,,,,,1991-12-30,F,,,,,73.1Z,NAF1993,N
# 999990625,00025,99999062500025,O,,NN,,,,true,1,,,,RTE,DE,MANOM,57100,THIONVILLE,,,57672,,,,,,,,,,,,,,,,,,,1995-11-30,F,,,,,27.3C,NAF1993,O
# 999990666,00011,99999066600011,O,1986-05-15,,,,,false,4,,10,,RUE,CHAUCHAT,75009,PARIS,9,,,75109,,,,,,,,,,,,,,,,,,,1997-12-03,F,,,,,66.0A,NAF1993,N
# 999990666,00029,99999066600029,O,1997-12-03,,,,,false,3,,2,,RUE,PILLET,WILL,75009,PARIS,9,,,75109,,,,,,,,,,,,,,,,,,,2000-07-01,F,,,,,66.0A,NAF1993,N
corelibs.cleanse.strip_chaining(string, **kwargs)

Description

Wrapper de la fonction corelibs.cleanse.strip() permettant de chaîner cette dernière, de manière à faciliter la lisibilité et optimiser les traitements de données
Parameters
  • string – chaine de caractères à traiter

  • kwargs – liste dynamique d’arguments nommés. Cette liste accepte toutes les données loufoques. Cependant seules les fonctions corelibs.cleanse.strip() seront traitées (cf. Exemple pour plus de précisions)

Returns

chaine de caractères transformée

Exemple :

# replace_chaining.py
# %%
from corelibs import cleanse as cls, lazy as lz


# %%
# enchainement (plus lisible)
string = """
999990583;00133;99999058300133;O;1993-01-01;NN;;;2006-11-25T22:25:28;false;4;CENTRALE DE MIJANES;;;;;09120;VARILHES;;;09324;;;;;;;;;;;;;;;;;;;2004-06-30;F;;;;;40.1A;NAFRev1;N
999990583;00158;99999058300158;O;1998-09-07;NN;;;2019-11-14T14:00:51;true;3;PARC TECHNOL LA PARDIEU;6;;RUE;CONDORCET;63000;CLERMONT-FERRAND;;;63113;;;;;;;;;;;;;;;;;;;2004-06-30;F;;;;;28.4A;NAFRev1;N
999990609;00011;99999060900011;O;;NN;;;;true;1;;4;;PL;DE LA PYRAMIDE;92800;PUTEAUX;;;92062;;;;;;;;;;;;;;;;;;;1991-12-30;F;;;;;73.1Z;NAF1993;N
999990625;00025;99999062500025;O;;NN;;;;true;1;;;;RTE;DE MANOM;57100;THIONVILLE;;;57672;;;;;;;;;;;;;;;;;;;1995-11-30;F;;;;;27.3C;NAF1993;O
999990641;00014;99999064100014;O;;NN;;;;true;1;;99;;BD;DE GRENELLE;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;1986-06-15;F;;;;;65.01;NAP;N
999990666;00011;99999066600011;O;1986-05-15;;;;;false;4;;10;;RUE;CHAUCHAT;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;1997-12-03;F;;;;;66.0A;NAF1993;N
999990666;00029;99999066600029;O;1997-12-03;;;;;false;3;;2;;RUE;PILLET WILL;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;2000-07-01;F;;;;;66.0A;NAF1993;N
999990666;00037;99999066600037;O;2000-07-01;NN;;;2020-05-20T03:34:55;true;3;8 A 10;8;;RUE;D'ASTORG;75008;PARIS 8;;;75108;;;;;;;;;;;;;;;;;;;2008-01-01;A;;;;;65.11Z;NAFRev2;N
999990682;00034;99999068200034;O;2001-09-18;NN;;;;true;3;LE PONANT DE PARIS;27;;RUE;LEBLANC;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;2003-12-18;F;;;;;65.2E;NAFRev1;N
999992357;00015;99999235700015;O;2003-12-31;01;2017;;2019-06-24T14:13:19;true;5;;6;;RUE;DE L ETOILE;80090;AMIENS;;;80021;;;;;;;;;;;;;;;;;;;2012-01-22;A;;;;;81.10Z;NAFRev2;O
"""
res = cls.strip_chaining(
    string,
    arg_dynamique="Coucou",  # ne sera pas traité car n'est pas un callable
    function_dynamique=lz.get_home,  # callable mais ne sera pas non plus traité car n'est pas un strip()
    # liste infinie dynamique des fonctions appelables à la suite... (si utile et pertinent)
    udf_1=lambda x: cls.strip(x, search=r"\d{9};00[1-9]{3};.*", non_printable_char=False),  # suppression de tous les siren avec un NIC 00XXXX (en ne gardant plus que les NIC à 000XX)
    udf_2=lambda x: cls.strip(x, search=b"\x3B", non_printable_char=False, replace=b"\x2C"),
    udf_3=lambda x: cls.strip(x, search=r".*,(NAFRev\d*|NAP).*", non_printable_char=False)  # suppression de tous les NAF non définitifs ou les valeurs NAP qui semblent être incorrects
)
print(res)
# affiche
# 999990609,00011,99999060900011,O,,NN,,,,true,1,,4,,PL,DE,LA,PYRAMIDE,92800,PUTEAUX,,,92062,,,,,,,,,,,,,,,,,,,1991-12-30,F,,,,,73.1Z,NAF1993,N
# 999990625,00025,99999062500025,O,,NN,,,,true,1,,,,RTE,DE,MANOM,57100,THIONVILLE,,,57672,,,,,,,,,,,,,,,,,,,1995-11-30,F,,,,,27.3C,NAF1993,O
# 999990666,00011,99999066600011,O,1986-05-15,,,,,false,4,,10,,RUE,CHAUCHAT,75009,PARIS,9,,,75109,,,,,,,,,,,,,,,,,,,1997-12-03,F,,,,,66.0A,NAF1993,N
# 999990666,00029,99999066600029,O,1997-12-03,,,,,false,3,,2,,RUE,PILLET,WILL,75009,PARIS,9,,,75109,,,,,,,,,,,,,,,,,,,2000-07-01,F,,,,,66.0A,NAF1993,N


# %%
# sinon en version non enchainée, 1ère possibilité (imbrication)
print(
    cls.strip(
        cls.strip(
            cls.strip(
                string,
                search=r"\d{9};00[1-9]{3};.*",  # suppression de tous les siren avec un NIC 00XXXX (en ne gardant plus que les NIC à 000XX)
                non_printable_char=False
            ),
            search=b"\x3B",
            replace=b"\x2C",
            non_printable_char=False
        ),
        search=r".*,(NAFRev\d*|NAP).*",  # suppression de tous les NAF non définitifs ou les valeurs NAP qui semblent être incorrects
        non_printable_char=False
    )
)
# affiche bien l'attendu
# 999990609,00011,99999060900011,O,,NN,,,,true,1,,4,,PL,DE,LA,PYRAMIDE,92800,PUTEAUX,,,92062,,,,,,,,,,,,,,,,,,,1991-12-30,F,,,,,73.1Z,NAF1993,N
# 999990625,00025,99999062500025,O,,NN,,,,true,1,,,,RTE,DE,MANOM,57100,THIONVILLE,,,57672,,,,,,,,,,,,,,,,,,,1995-11-30,F,,,,,27.3C,NAF1993,O
# 999990666,00011,99999066600011,O,1986-05-15,,,,,false,4,,10,,RUE,CHAUCHAT,75009,PARIS,9,,,75109,,,,,,,,,,,,,,,,,,,1997-12-03,F,,,,,66.0A,NAF1993,N
# 999990666,00029,99999066600029,O,1997-12-03,,,,,false,3,,2,,RUE,PILLET,WILL,75009,PARIS,9,,,75109,,,,,,,,,,,,,,,,,,,2000-07-01,F,,,,,66.0A,NAF1993,N


# %%
# sinon en version non enchainée, 2ème possibilité (avec étapes intermédiaires)
etape_1 = cls.strip(
    string,
    search=r"\d{9};00[1-9]{3};.*",  # suppression de tous les siren avec un NIC 00XXXX (en ne gardant plus que les NIC à 000XX)
    non_printable_char=False
)

etape_2 = cls.strip(
    etape_1,
    search=b"\x3B",
    replace=b"\x2C",
    non_printable_char=False
)

print(
    cls.strip(
        etape_2,
        search=r".*,(NAFRev\d*|NAP).*",  # suppression de tous les NAF non définitifs ou les valeurs NAP qui semblent être incorrects
        non_printable_char=False
    )
)
# affiche bien l'attendu
# 999990609,00011,99999060900011,O,,NN,,,,true,1,,4,,PL,DE,LA,PYRAMIDE,92800,PUTEAUX,,,92062,,,,,,,,,,,,,,,,,,,1991-12-30,F,,,,,73.1Z,NAF1993,N
# 999990625,00025,99999062500025,O,,NN,,,,true,1,,,,RTE,DE,MANOM,57100,THIONVILLE,,,57672,,,,,,,,,,,,,,,,,,,1995-11-30,F,,,,,27.3C,NAF1993,O
# 999990666,00011,99999066600011,O,1986-05-15,,,,,false,4,,10,,RUE,CHAUCHAT,75009,PARIS,9,,,75109,,,,,,,,,,,,,,,,,,,1997-12-03,F,,,,,66.0A,NAF1993,N
# 999990666,00029,99999066600029,O,1997-12-03,,,,,false,3,,2,,RUE,PILLET,WILL,75009,PARIS,9,,,75109,,,,,,,,,,,,,,,,,,,2000-07-01,F,,,,,66.0A,NAF1993,N

Module data

Description générale

Module pour gérer tout ce qui se rapporte à la manipulation des données

corelibs.data.append_files(files_2_append, out_file_path, source_dir_path=None, skip_header=True, chunk=65536)

Description

Permet de concaténer des fichiers plats
Parameters
  • files_2_append – indique les fichiers à concaténer

  • out_file_path – indique le nom du fichier de sortie avec son chemin absolu

  • source_dir_path – indique le chemin absolu des fichiers sources (optionnel pour factoriser l’argument files_2_append)

  • skip_header

    indique s’il faut ou non écarter la ligne d’entête

    valeurs possibles: False/True
    valeur par défaut: True

  • chunk

    indique le nombre de lignes à lire par cycle de lecture cf. DEFAULT_BUFFER_CHUNK_SIZE dans Installation & Mise à jour

    valeurs possibles: entier/None
    valeur par défaut: DEFAULT_BUFFER_CHUNK_SIZE

Returns

rien

Exemple :

# append_files.py
# %%
from corelibs import data, log, tools


# %%
# stress test sur un fichier des établissements d'open data gouv.fr
# taille fichier : 29 928 195 lignes, pour un poids total de 4.88 Go
# concaténation 3 fois le fichier originale
@log.timing()
@log.status_bar()
def stress_test():
    data.append_files(
        source_dir_path=r"D:\OneDrive\Desktop\StockEtablissement_utf8",  # factorisation chemin fichiers sources
        files_2_append=(
            "StockEtablissement_utf8.csv",
            "StockEtablissement_utf8.tsv",
            "fichier_inexistant.toto",  # => lève une erreur, sans action et continue...
            r"D:\OneDrive\Documents\_TEST_\StockEtablissement_utf8.csv"  # fichier se trouvant à un autre emplacement
        ),
        out_file_path=r"D:\OneDrive\Documents\_TEST_\StockEtablissement_utf8_A3T.csv"
    )


stress_test()  # Durée exécution : 00:04:10.60


# %%
# Vérification poids fichier
print(tools.get_file_properties(r"D:\OneDrive\Documents\_TEST_\StockEtablissement_utf8_A3T.csv"))
# FileProperties(st_mode=33206, st_ino=3659174697355776, st_dev=3199390331, st_nlink=1, st_uid='miche', st_gid=0, st_size='14.65 Go', st_atime='20/01/2021 23:44:12', st_mtime='20/01/2021 23:44:12', st_ctime='20/01/2021 23:18:42')


# %%
# Vérification nb de lignes
@log.timing()
@log.status_bar()
def stress_test():
    print(
        tools.get_total_lines_in_file(
            r"D:\OneDrive\Documents\_TEST_\StockEtablissement_utf8_A3T.csv"
        )
    )


stress_test()  # Durée exécution : 00:02:40.59 pour lire et compter 89 784 581 de lignes


# %%
# %%time
print(tools.get_fingerprint(r"D:\OneDrive\Documents\_TEST_\StockEtablissement_utf8_A3T.csv"))
# 9ebe16404b9654fa790ed16ebcf5d4d6afd44ff62bb9a98a22533783dc0200f6 (2min 29s)


# %%
# nettoyage fichier, puisque la concaténation est faite sur des fichiers csv et tsv...
@log.timing()
@log.status_bar()
def stress_test():
    data.replace_in_file(
        path=r"D:\OneDrive\Documents\_TEST_\StockEtablissement_utf8_A3T.csv",
        pattern=b"\x09",  # <=> tab
        replace=b"\x2C",  # <=> ,
        ignore_errors=True
    )


stress_test()  # Durée exécution : 00:05:47.55 pour nettoyer 90 millions de lignes
corelibs.data.has_same_header(file_path, file_2_compare, separator=';')

Description

Permet de vérifier si 2 fichiers plats ont la même entête
Parameters
  • file_path – indique le fichier de référence avec son chemin absolu.

  • file_2_compare – indique le fichier à comparer avec son chemin absolu.

  • separator – indique le caractère séparateur.

Returns

tuple nommé avec comme attributs :
  • result (booléen)

  • cause (motif)

Exemple :

# has_same_header.py
# %%
from corelibs import data

# %%
# Test OK
_ = data.has_same_header(
    r"C:\Users\miki\AppData\Local\Temp\tmpa1trgjyy_corelibs",
    r"C:\Users\miki\AppData\Local\Temp\tmpzhoxgdc2_corelibs"
)
print(f"Résultat : {_.result}", f" - Infos : {_.cause}")
# Résultat : True  - Infos : {'hype': 'o(^  ^ o) Tout est OK, YEAH!!! (o ^  ^)o'}


# %%
# Test KO 1ère possibilité
_ = data.has_same_header(
    r"C:\Users\miki\AppData\Local\Temp\tmpa1trgjyy_corelibs",
    r"C:\Users\miki\AppData\Local\Temp\tmpzhoxgdc2_corelibs"
)
print(f"Résultat : {_.result}", f" - Infos : {_.cause}")
# Résultat : False  - Infos : {
#    'cause': 'Différence format',
#    'columns difference': ['owner', 'Created Time', 'Finger Print'],
#    'file': [
#        '#',
#        'File Type',
#        'File Name',
#        'Technical ID',
#        'owner',
#        'Last Access',
#        'Last Modification',
#        'Created Time',
#        'Bytes',
#        'KiloBytes',
#        'MegaBytes',
#        'GigaBytes',
#        'Finger Print'
#    ],
#    'file 2 compare': [
#        '#',
#        'File Type',
#        'File Name',
#        'Technical ID',
#        'Owner',
#        'Last Access',
#        'Last Modification',
#        'OS Time',
#        'Bytes',
#        'KiloBytes',
#        'MegaBytes',
#        'GigaBytes',
#        'Fingerprint'
#    ]
# }


# %%
# Test KO 2ème possibilité
_ = data.has_same_header(
    r"C:\Users\miki\AppData\Local\Temp\tmpa1trgjyy_corelibs",
    r"C:\Users\miki\AppData\Local\Temp\tmpzhoxgdc2_corelibs"
)
print(f"Résultat : {_.result}", f" - Infos : {_.cause}")
# Résultat : False  - Infos : {'cause': 'Longueur différente', 'file': '14 colonnes', 'file 2 compare': '13 colonnes'}
corelibs.data.head(file_path, chunk=65536, skip_header=False, start_file=False, encoding='Latin-1', out_encoding='Latin-1')

Description

Permet d’extraire les premières lignes d’un gros fichier plat.
Parameters
  • file_path – indique l’emplacement en chemin absolu du fichier plat à traiter.

  • chunk – indique le nombre de lignes à extraire cf. DEFAULT_BUFFER_CHUNK_SIZE dans Installation & Mise à jour

  • skip_header

    indique si l’extraction doit écarter l’entête du fichier.

    valeurs possibles: False/True
    valeur par défaut: False

  • start_file

    indique s’il faut lancer l’application liée par défaut au format du fichier, après extraction.

    valeurs possibles: False/True
    valeur par défaut: False

  • encoding – indique l’encodage à la lecture

  • out_encoding – indique l’encodage à l’écriture (identique à l’encoding de lecture par défaut)

Returns

le chemin du fichier extrait (à la même racine, avec le suffixe “_head_preview”)
ou
None si problème

Exemple :

# head.py
# %%
from corelibs import data, log


# stress test sur un fichier des établissements d'open data gouv.fr
# taille fichier : 29 928 195 lignes, pour un poids total de 4.88 Go
@log.timing()
@log.status_bar()
def stress_test():
    return data.head(r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8.csv", start_file=True)


preview_path = stress_test()  # Durée exécution : 00:00:00.58 pour récupérer 65K/30 millions de lignes


# %%
# prévisualisation en mode web...
data.preview(preview_path, separator=",")
corelibs.data.preview(obj, separator=';', allow_cell_edits=True, subprocess=True)

Description

Permet de visionner et manipuler rapidement les données extraites ou des données agrégées dans une application web.

Warning

Attention aux erreurs de type Mémoire insuffisante. La manipulation des données sur l’application web devrait se faire sur un nombre restreint d’observations et spécialement en fonction des ressources propres au PC sur lequel est lancé les traitements python.
Parameters
  • obj – indique l’emplacement en chemin absolu du fichier plat ou le dataframe pandas à prévisualiser.

  • separator – indique le caractère séparateur.

  • allow_cell_edits

    indique la possibilité d’éditer les cellules lues.

    valeurs possibles: False/True
    valeur par défaut: True

  • subprocess

    indique si l’exécution se fait en mode threading ou non. Le comportement par défaut va tuer automatiquement le process si il n’est plus utilisé ou si l’exécution de la pile principale est finie (ce paramétrage fonctionne sous iPython, Jupyter & la console Python mais ne fontionne pas sur un terminal standard ou un terminal émulé - PyCharm par exemple - où il faudrait désactiver le threading)

    valeurs possibles: False/True
    valeur par défaut: True

Returns

rien

Exemple :

# preview.py
# %%
import pandas as pd

from corelibs import data

# %%  # prévisualisation standard à partir d'un fichier plat
# %%time
data.preview(
    r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8_tail_preview.tsv",
    separator="\t",
)
# 32s pour lire et afficher 1 048 576 de lignes
# 6min 27s pour lire et afficher 10 000 000 de lignes


# %%  # prévisualisation à partir d'un dataframe pandas
# %%time
df = pd.read_csv(
    r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8_tail_preview.tsv",
    sep="\t",
    dtype=str,
    engine="python",
    index_col=False
).fillna("")
data.preview(df)  # 1min 26s pour lire 1 048 576 de lignes
_images/dtale_preview.png
corelibs.data.replace_in_file(path, pattern, replace, regex_flag=None, out_file_path=None, ignore_errors=False, encoding='Latin-1', out_encoding='Latin-1')

Description

Permet de scanner un fichier et de remplacer tout son contenu.
Parameters
  • path – indique l’emplacement en chemin absolu du fichier plat à traiter.

  • pattern – indique la chaine de caractères ou le modèle regex à chercher.

  • replace – indique la chaine de caractères ou le modèle regex en remplacement.

  • regex_flag – indique les flags à utiliser. cf. Liens utiles, Librairie regex pour plus de détails

  • out_file_path – indique le fichier de sortie si nécessaire.

  • ignore_errors

    indique si l’ouverture des fichiers doit ignorer ou non les erreurs d’encodage de type “byte 0xff in position 0” qui survient lors d’un décodage caractère encodé en utf-16 alors que la lecture du fichier se fait avec le code utf-8.

    valeurs possibles: False/True
    valeur par défaut: False

  • encoding – indique l’encodage à la lecture

  • encoding – indique l’encodage à l’écriture (identique à l’encoding de lecture par défaut)

Returns

rien…

Exemple :

# replace_in_file.py
# %%
import re

from corelibs import config, data, log

# %%
# contentu fichier source "termcolorlog_20201208_180401.LOG" avant
# Tuesday 08 December 2020 - Ma log à moi que j'ai [termcolorlog.py:1] <>          INFO : Bonjour, ceci est un test personnalisé niveau INFO
# Tuesday 08 December 2020 - Ma log à moi que j'ai [termcolorlog.py:1] <>       WARNING : Bonjour, ceci est un test personnalisé niveau WARNING
# Tuesday 08 December 2020 - Ma log à moi que j'ai [termcolorlog.py:1] <>         ERROR : Bonjour, ceci est un test personnalisé niveau ERROR
# Tuesday 08 December 2020 - Ma log à moi que j'ai [termcolorlog.py:1] <>      CRITICAL : Bonjour, ceci est un test personnalisé niveau CRITIQUE
#######

# remplacement avec écrasement du fichier source
data.replace_in_file(
    path=r"D:\OneDrive\Documents\_TEST_\__LOGS__\termcolorlog_20201208_180401.LOG",
    pattern="Bonjour,",
    replace="Au revoir !"
)
# contenu fichier source "termcolorlog_20201208_180401.LOG" après
# Tuesday 08 December 2020 - Ma log à moi que j'ai [termcolorlog.py:1] <>          INFO : Au revoir ! ceci est un test personnalisé niveau INFO
# Tuesday 08 December 2020 - Ma log à moi que j'ai [termcolorlog.py:1] <>       WARNING : Au revoir ! ceci est un test personnalisé niveau WARNING
# Tuesday 08 December 2020 - Ma log à moi que j'ai [termcolorlog.py:1] <>         ERROR : Au revoir ! ceci est un test personnalisé niveau ERROR
# Tuesday 08 December 2020 - Ma log à moi que j'ai [termcolorlog.py:1] <>      CRITICAL : Au revoir ! ceci est un test personnalisé niveau CRITIQUE


# %%
# remplacement via modèle regex avec écrasement
data.replace_in_file(
    path=r"D:\OneDrive\Documents\_TEST_\__LOGS__\termcolorlog_20201208_180401.LOG",
    pattern=r"^(Tuesday) (08) (December) (2020)(.*)(Au revoir !)(.*)$",
    replace=r"\4/12/\2\5Bonjour,\7 - modifié par regex =}"
)
# contenu fichier source "termcolorlog_20201208_180401.LOG" après 2ème passage
# 2020/12/08 - Ma log à moi que j'ai [termcolorlog.py:1] <>          INFO : Bonjour, ceci est un test personnalisé niveau INFO - modifié par regex =}
# 2020/12/08 - Ma log à moi que j'ai [termcolorlog.py:1] <>       WARNING : Bonjour, ceci est un test personnalisé niveau WARNING - modifié par regex =}
# 2020/12/08 - Ma log à moi que j'ai [termcolorlog.py:1] <>         ERROR : Bonjour, ceci est un test personnalisé niveau ERROR - modifié par regex =}
# 2020/12/08 - Ma log à moi que j'ai [termcolorlog.py:1] <>      CRITICAL : Bonjour, ceci est un test personnalisé niveau CRITIQUE - modifié par regex =}


# %%
# remplacement avec redirection dans un autre fichier de sortie
data.replace_in_file(
    path=r"D:\OneDrive\Documents\_TEST_\__LOGS__\termcolorlog_20201208_180401.LOG",
    pattern=r"^(.*)(INFO|WARNING|ERROR|CRITICAL)( : )(.*)$",
    replace=r"\4",
    out_file_path=r"D:\OneDrive\Documents\_TEST_\__LOGS__\Nouveau Fichier Nettoyé.TXT"
)
# contenu fichier cible "Nouveau Fichier Nettoyé.TXT" après traitement
# Bonjour, ceci est un test personnalisé niveau INFO - modifié par regex =}
# Bonjour, ceci est un test personnalisé niveau WARNING - modifié par regex =}
# Bonjour, ceci est un test personnalisé niveau ERROR - modifié par regex =}
# Bonjour, ceci est un test personnalisé niveau CRITIQUE - modifié par regex =}

# %%
# utilisation flags regex
data.replace_in_file(
    path=r"D:\OneDrive\Documents\_TEST_\__LOGS__\termcolorlog_20201208_180401.LOG",
    pattern=r"^(.*)(INFO|WARNING|ERROR|CRITICAL)( : )(.*)$",
    replace=r"\4",
    out_file_path=r"D:\OneDrive\Documents\_TEST_\__LOGS__\Nouveau Fichier Nettoyé Bis.TXT",
    regex_flag=re.M | re.I
)
# création nouveau fichier "Nouveau Fichier Nettoyé Bis.TXT" traité

# %%
# remplacement fichier avec redirection dans un autre fichier, des espaces par des ;
data.replace_in_file(
    path=r"D:\OneDrive\Documents\_TEST_\__LOGS__\termcolorlog_20201208_180401.LOG",
    pattern=b"\x20",
    replace=b"\x3B",
    out_file_path=r"D:\OneDrive\Documents\_TEST_\__LOGS__\Nouveau Fichier Nettoyé escape.TXT"
)
# contenu fichier cible "Nouveau Fichier Nettoyé escape.TXT" après traitement
# 2020/12/08;-;Ma;log;à;moi;que;j'ai;[termcolorlog.py:1];<>;;;;;;;;;;INFO;:;Au;revoir;!;ceci;est;un;test;personnalisé;niveau;INFO;-;modifié;par;regex;=}
# 2020/12/08;-;Ma;log;à;moi;que;j'ai;[termcolorlog.py:1];<>;;;;;;;WARNING;:;Au;revoir;!;ceci;est;un;test;personnalisé;niveau;WARNING;-;modifié;par;regex;=}
# 2020/12/08;-;Ma;log;à;moi;que;j'ai;[termcolorlog.py:1];<>;;;;;;;;;ERROR;:;Au;revoir;!;ceci;est;un;test;personnalisé;niveau;ERROR;-;modifié;par;regex;=}
# 2020/12/08;-;Ma;log;à;moi;que;j'ai;[termcolorlog.py:1];<>;;;;;;CRITICAL;:;Au;revoir;!;ceci;est;un;test;personnalisé;niveau;CRITIQUE;-;modifié;par;regex;=}

# %%
config.DEFAULT_ENCODING_FILE = "utf-8"  # écrasement du format par défaut en utf-8 comme demandé par open data

# stress test sur un fichier des établissements d'open data gouv.fr
# taille fichier : 29 928 195 lignes, pour un poids total de 4.88 Go
# 10 dernière lignes du fichier original de 5244591168 octets <=> 4.88 Go
# 999990583,00133,99999058300133,O,1993-01-01,NN,,,2006-11-25T22:25:28,false,4,CENTRALE DE MIJANES,,,,,09120,VARILHES,,,09324,,,,,,,,,,,,,,,,,,,2004-06-30,F,,,,,40.1A,NAFRev1,N
# 999990583,00158,99999058300158,O,1998-09-07,NN,,,2019-11-14T14:00:51,true,3,PARC TECHNOL LA PARDIEU,6,,RUE,CONDORCET,63000,CLERMONT-FERRAND,,,63113,,,,,,,,,,,,,,,,,,,2004-06-30,F,,,,,28.4A,NAFRev1,N
# 999990609,00011,99999060900011,O,,NN,,,,true,1,,4,,PL,DE LA PYRAMIDE,92800,PUTEAUX,,,92062,,,,,,,,,,,,,,,,,,,1991-12-30,F,,,,,73.1Z,NAF1993,N
# 999990625,00025,99999062500025,O,,NN,,,,true,1,,,,RTE,DE MANOM,57100,THIONVILLE,,,57672,,,,,,,,,,,,,,,,,,,1995-11-30,F,,,,,27.3C,NAF1993,O
# 999990641,00014,99999064100014,O,,NN,,,,true,1,,99,,BD,DE GRENELLE,75015,PARIS 15,,,75115,,,,,,,,,,,,,,,,,,,1986-06-15,F,,,,,65.01,NAP,N
# 999990666,00011,99999066600011,O,1986-05-15,,,,,false,4,,10,,RUE,CHAUCHAT,75009,PARIS 9,,,75109,,,,,,,,,,,,,,,,,,,1997-12-03,F,,,,,66.0A,NAF1993,N
# 999990666,00029,99999066600029,O,1997-12-03,,,,,false,3,,2,,RUE,PILLET WILL,75009,PARIS 9,,,75109,,,,,,,,,,,,,,,,,,,2000-07-01,F,,,,,66.0A,NAF1993,N
# 999990666,00037,99999066600037,O,2000-07-01,NN,,,2020-05-20T03:34:55,true,3,8 A 10,8,,RUE,D'ASTORG,75008,PARIS 8,,,75108,,,,,,,,,,,,,,,,,,,2008-01-01,A,,,,,65.11Z,NAFRev2,N
# 999990682,00034,99999068200034,O,2001-09-18,NN,,,,true,3,LE PONANT DE PARIS,27,,RUE,LEBLANC,75015,PARIS 15,,,75115,,,,,,,,,,,,,,,,,,,2003-12-18,F,,,,,65.2E,NAFRev1,N
# 999992357,00015,99999235700015,O,2003-12-31,01,2017,,2019-06-24T14:13:19,true,5,,6,,RUE,DE L ETOILE,80090,AMIENS,,,80021,,,,,,,,,,,,,,,,,,,2012-01-22,A,,,,,81.10Z,NAFRev2,O


@log.timing()
@log.status_bar()
def stress_test():
    # sortie en fichier tabulé
    data.replace_in_file(
        path=r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8.csv",
        pattern=b"\x2C",  # <=> ,
        replace=b"\x09",  # <=> tab
        out_file_path=r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8.tsv",
        ignore_errors=True
    )


stress_test()  # Durée exécution : 00:01:32.17


# %%
@log.timing()
@log.status_bar()
def stress_test():
    # écrire sur le même fichier de sorti en tant que csv ;
    data.replace_in_file(
        path=r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8.tsv",
        pattern=b"\x09",  # <=> tab
        replace=b"\x3B",  # <=> ;
        ignore_errors=True
    )


stress_test()  # Durée exécution : 00:01:20.13
# 10 dernière lignes du fichier TSV
# 999990583;00133;99999058300133;O;1993-01-01;NN;;;2006-11-25T22:25:28;false;4;CENTRALE DE MIJANES;;;;;09120;VARILHES;;;09324;;;;;;;;;;;;;;;;;;;2004-06-30;F;;;;;40.1A;NAFRev1;N
# 999990583;00158;99999058300158;O;1998-09-07;NN;;;2019-11-14T14:00:51;true;3;PARC TECHNOL LA PARDIEU;6;;RUE;CONDORCET;63000;CLERMONT-FERRAND;;;63113;;;;;;;;;;;;;;;;;;;2004-06-30;F;;;;;28.4A;NAFRev1;N
# 999990609;00011;99999060900011;O;;NN;;;;true;1;;4;;PL;DE LA PYRAMIDE;92800;PUTEAUX;;;92062;;;;;;;;;;;;;;;;;;;1991-12-30;F;;;;;73.1Z;NAF1993;N
# 999990625;00025;99999062500025;O;;NN;;;;true;1;;;;RTE;DE MANOM;57100;THIONVILLE;;;57672;;;;;;;;;;;;;;;;;;;1995-11-30;F;;;;;27.3C;NAF1993;O
# 999990641;00014;99999064100014;O;;NN;;;;true;1;;99;;BD;DE GRENELLE;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;1986-06-15;F;;;;;65.01;NAP;N
# 999990666;00011;99999066600011;O;1986-05-15;;;;;false;4;;10;;RUE;CHAUCHAT;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;1997-12-03;F;;;;;66.0A;NAF1993;N
# 999990666;00029;99999066600029;O;1997-12-03;;;;;false;3;;2;;RUE;PILLET WILL;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;2000-07-01;F;;;;;66.0A;NAF1993;N
# 999990666;00037;99999066600037;O;2000-07-01;NN;;;2020-05-20T03:34:55;true;3;8 A 10;8;;RUE;D'ASTORG;75008;PARIS 8;;;75108;;;;;;;;;;;;;;;;;;;2008-01-01;A;;;;;65.11Z;NAFRev2;N
# 999990682;00034;99999068200034;O;2001-09-18;NN;;;;true;3;LE PONANT DE PARIS;27;;RUE;LEBLANC;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;2003-12-18;F;;;;;65.2E;NAFRev1;N
# 999992357;00015;99999235700015;O;2003-12-31;01;2017;;2019-06-24T14:13:19;true;5;;6;;RUE;DE L ETOILE;80090;AMIENS;;;80021;;;;;;;;;;;;;;;;;;;2012-01-22;A;;;;;81.10Z;NAFRev2;O


# %%
@log.timing()
@log.status_bar()
def stress_test():
    # remplacement dans le fichier csv nouvellement formé des dates ayant un format AAAA-MM-DD par DD/MM/AAAA uniquement
    # les champs datetime ayant le format AAAA-MM-DDTHH:MM:SS ne sont pas transformés
    data.replace_in_file(
        path=r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8.tsv",
        pattern=r"\b(\d{4})-(\d{2})-(\d{2})\b",
        replace=r"\3/\2/\1",
        ignore_errors=True
    )


stress_test()  # Durée exécution : 00:05:50.94
# 10 dernière lignes du fichier TSV
# 999990583;00133;99999058300133;O;01/01/1993;NN;;;2006-11-25T22:25:28;false;4;CENTRALE DE MIJANES;;;;;09120;VARILHES;;;09324;;;;;;;;;;;;;;;;;;;30/06/2004;F;;;;;40.1A;NAFRev1;N
# 999990583;00158;99999058300158;O;07/09/1998;NN;;;2019-11-14T14:00:51;true;3;PARC TECHNOL LA PARDIEU;6;;RUE;CONDORCET;63000;CLERMONT-FERRAND;;;63113;;;;;;;;;;;;;;;;;;;30/06/2004;F;;;;;28.4A;NAFRev1;N
# 999990609;00011;99999060900011;O;;NN;;;;true;1;;4;;PL;DE LA PYRAMIDE;92800;PUTEAUX;;;92062;;;;;;;;;;;;;;;;;;;30/12/1991;F;;;;;73.1Z;NAF1993;N
# 999990625;00025;99999062500025;O;;NN;;;;true;1;;;;RTE;DE MANOM;57100;THIONVILLE;;;57672;;;;;;;;;;;;;;;;;;;30/11/1995;F;;;;;27.3C;NAF1993;O
# 999990641;00014;99999064100014;O;;NN;;;;true;1;;99;;BD;DE GRENELLE;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;15/06/1986;F;;;;;65.01;NAP;N
# 999990666;00011;99999066600011;O;15/05/1986;;;;;false;4;;10;;RUE;CHAUCHAT;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;03/12/1997;F;;;;;66.0A;NAF1993;N
# 999990666;00029;99999066600029;O;03/12/1997;;;;;false;3;;2;;RUE;PILLET WILL;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;01/07/2000;F;;;;;66.0A;NAF1993;N
# 999990666;00037;99999066600037;O;01/07/2000;NN;;;2020-05-20T03:34:55;true;3;8 A 10;8;;RUE;D'ASTORG;75008;PARIS 8;;;75108;;;;;;;;;;;;;;;;;;;01/01/2008;A;;;;;65.11Z;NAFRev2;N
# 999990682;00034;99999068200034;O;18/09/2001;NN;;;;true;3;LE PONANT DE PARIS;27;;RUE;LEBLANC;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;18/12/2003;F;;;;;65.2E;NAFRev1;N
# 999992357;00015;99999235700015;O;31/12/2003;01;2017;;2019-06-24T14:13:19;true;5;;6;;RUE;DE L ETOILE;80090;AMIENS;;;80021;;;;;;;;;;;;;;;;;;;22/01/2012;A;;;;;81.10Z;NAFRev2;O


# %%
# Pour changer le champs datetime ayant le format AAAA-MM-DDTHH:MM:SS il faut chaîner avec une nouvelle expression régulière
@log.timing()
@log.status_bar()
def stress_test():
    # remplacement les champs datetime ayant le format AAAA-MM-DDTHH:MM:SS ne sont pas transformés par DD/MM/AAAA HH:MM
    data.replace_in_file(
        path=r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8.tsv",
        pattern=r"\b(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\b",
        replace=r"\3/\2/\1 \4:\5",
        ignore_errors=True
    )


stress_test()  # Durée exécution : 00:05:00.71
# 10 dernière lignes du fichier TSV
# 999990583;00133;99999058300133;O;01/01/1993;NN;;;25/11/2006 22:25;false;4;CENTRALE DE MIJANES;;;;;09120;VARILHES;;;09324;;;;;;;;;;;;;;;;;;;30/06/2004;F;;;;;40.1A;NAFRev1;N
# 999990583;00158;99999058300158;O;07/09/1998;NN;;;14/11/2019 14:00;true;3;PARC TECHNOL LA PARDIEU;6;;RUE;CONDORCET;63000;CLERMONT-FERRAND;;;63113;;;;;;;;;;;;;;;;;;;30/06/2004;F;;;;;28.4A;NAFRev1;N
# 999990609;00011;99999060900011;O;;NN;;;;true;1;;4;;PL;DE LA PYRAMIDE;92800;PUTEAUX;;;92062;;;;;;;;;;;;;;;;;;;30/12/1991;F;;;;;73.1Z;NAF1993;N
# 999990625;00025;99999062500025;O;;NN;;;;true;1;;;;RTE;DE MANOM;57100;THIONVILLE;;;57672;;;;;;;;;;;;;;;;;;;30/11/1995;F;;;;;27.3C;NAF1993;O
# 999990641;00014;99999064100014;O;;NN;;;;true;1;;99;;BD;DE GRENELLE;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;15/06/1986;F;;;;;65.01;NAP;N
# 999990666;00011;99999066600011;O;15/05/1986;;;;;false;4;;10;;RUE;CHAUCHAT;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;03/12/1997;F;;;;;66.0A;NAF1993;N
# 999990666;00029;99999066600029;O;03/12/1997;;;;;false;3;;2;;RUE;PILLET WILL;75009;PARIS 9;;;75109;;;;;;;;;;;;;;;;;;;01/07/2000;F;;;;;66.0A;NAF1993;N
# 999990666;00037;99999066600037;O;01/07/2000;NN;;;20/05/2020 03:34;true;3;8 A 10;8;;RUE;D'ASTORG;75008;PARIS 8;;;75108;;;;;;;;;;;;;;;;;;;01/01/2008;A;;;;;65.11Z;NAFRev2;N
# 999990682;00034;99999068200034;O;18/09/2001;NN;;;;true;3;LE PONANT DE PARIS;27;;RUE;LEBLANC;75015;PARIS 15;;;75115;;;;;;;;;;;;;;;;;;;18/12/2003;F;;;;;65.2E;NAFRev1;N
# 999992357;00015;99999235700015;O;31/12/2003;01;2017;;24/06/2019 14:13;true;5;;6;;RUE;DE L ETOILE;80090;AMIENS;;;80021;;;;;;;;;;;;;;;;;;;22/01/2012;A;;;;;81.10Z;NAFRev2;O
corelibs.data.split_file(file_path, skip_header=False, start=None, chunk=65536, suffix='_part_')

Description

Permet de scinder un fichier plat volumineux en plusieurs petits fichiers
Parameters
  • file_path – indique l’emplacement en chemin absolu du fichier plat à scinder.

  • skip_header

    indique s’il faut ou non écarter la ligne d’entête.

    valeurs possibles: False/True
    valeur par défaut: False

  • start – indique si besoin, la première ligne à partir de laquelle l’extraction va démarrer

  • chunk

    indique le nombre de lignes à scinder par fichier cf. DEFAULT_BUFFER_CHUNK_SIZE dans Installation & Mise à jour. Utilisé conjointement avec start cet argument sera pris comme l’offset d’arrêt pour extraire le fichier

    valeurs possibles: entier/None
    valeur par défaut: DEFAULT_BUFFER_CHUNK_SIZE

  • suffix – indique le suffix à appliquer aux fichiers scindés

Returns

rien

Exemple :

# split_file.py
# %%
import pandas as pd
from corelibs import data, log, tools


# %%
# stress test sur un fichier des établissements d'open data gouv.fr
# taille fichier : 29 928 195 lignes, pour un poids total de 4.88 Go
# découpage par tranche de 10 millions de lignes avec les entêtes (par défaut)
@log.timing()
@log.status_bar()
def stress_test():
    data.split_file(r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8.tsv", chunk=10000000)


stress_test()  # Durée exécution : 00:02:02.60


# %%
# %%time
# vérifications par scan modèles
wcl = tools.get_total_lines_in_folder(
    dir_2_scan=r"D:\OneDrive\Desktop\StockEtablissement_utf8",
    files_pattern="*_part_*.tsv"
)
print(wcl)  # Wcl(total_files=3, total_lines=29928199) - Durée exécution : 12.2s
# il y a un delta de 6 lignes avec la comparaison via pandas ci-dessous (ce delta est normal)


# %%
# vérifications entêtes
for i in range(3):
    data.head(
        r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8_part_{i}.tsv".format(i=i),
        chunk=10,
        start_file=True
    )


# %%
# vérification pandas
@log.timing()
@log.status_bar()
def verif_pandas():
    for i in range(3):
        df = pd.read_csv(
            r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8_part_{i}.tsv".format(i=i),
            sep="\t",
            dtype=str,
            usecols=["siren"],
            engine="python"
        )
        print(f"total lignes {i}", len(df))
        del df


verif_pandas()  # Durée exécution : 00:28:23.79
# total lignes 0 10000000
# total lignes 1 10000000
# total lignes 2 9928193


# %%
# découpage par tranche de 10 millions de lignes sans les entêtes
@log.timing()
@log.status_bar()
def stress_test():
    data.split_file(
        r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8.tsv",
        chunk=10000000,
        skip_header=True
    )


stress_test()  # Durée exécution : 00:02:25.85


# %%
# %%time
# découpage arbitraire, à partir d'une position pour un nombre de lignes données
data.split_file(
    r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8.tsv",
    start=10000000,
    chunk=100,
    suffix="_xtr_"
)  # Durée exécution 1min
corelibs.data.tail(file_path, chunk=65536, skip_header=False, start_file=False, encoding='Latin-1', out_encoding='Latin-1')

Description

Permet d’extraire les dernières lignes d’un gros fichier plat.
Parameters
  • file_path – indique l’emplacement en chemin absolu du fichier plat à traiter.

  • chunk – indique le nombre de lignes à extraire cf. DEFAULT_BUFFER_CHUNK_SIZE dans Installation & Mise à jour

  • skip_header

    indique si l’extraction doit écarter l’entête du fichier.

    valeurs possibles: False/True
    valeur par défaut: False

  • start_file

    indique s’il faut lancer l’application liée par défaut au format du fichier, après extraction.

    valeurs possibles: False/True
    valeur par défaut: False

  • encoding – indique l’encodage à la lecture

  • out_encoding – indique l’encodage à l’écriture (identique à l’encoding de lecture par défaut)

Returns

le chemin du fichier extrait (à la même racine, avec le suffixe “_tail_preview”)

Exemple :

# tail.py
# %%
from corelibs import data, log


# stress test sur un fichier des établissements d'open data gouv.fr
# taille fichier : 29 928 195 lignes, pour un poids total de 4.88 Go
@log.timing()
@log.status_bar()
def stress_test():
    return data.tail(
        r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8.tsv",
        chunk=1048576  # Excel > 2007
    )


preview_path = stress_test()  # Durée exécution : 00:00:03.23 pour extraire 1048576 de lignes sur les 30 millions de lignes du fichier sources

Module tools

Description générale

Module pour gérer les compressions, diagnostics, calcul des tailles occupées, sécuriser les données etc.

corelibs.tools.cipher(obj, password, delete_source_file=True)

Description

Permet de chiffrer une chaîne de caractère ou un fichier

Warning

Ce chiffrement est adapté pour protéger des fichiers plats contenant du code ou partie de codes sensibles, ou un fichier de petite taille.
Le mot de passe tient compte de la casse.
Parameters
  • obj – indique la chaine de caractère ou le chemin absolu du fichier à chiffrer

  • password – indique le mot de passe à appliquer lors du chiffrement

  • delete_source_file

    indique si le fichier source doit être supprimé après le chiffrement

    valeurs possibles: False/True
    valeur par défaut: True

Returns

True
ou
chaîne chiffrée (si obj est une chaîne de caractère)

Exemple :

# cipher.py
# %%
from corelibs import tools

cle_secrete = "Clé secrète de la morkitu! • 28Nov2013KT"

# %%
# chiffrement chaine de caractères
message = """
import corelibs

dict = {"msg": "Hello Kim", "from": "papa"}
print("message encapsulé chiffré", "•"*31, dict, dict["msg"], corelibs)

corelibs.lazy.merge_dictionaries(dict, {"from": "papa ❤ =}"})
"""

message_chiffre = tools.cipher(message, cle_secrete)
print(message_chiffre)
# affiche le binaire suivant
# b'x\xda\x1d\xc1k\xb3B@\x00\x00\xd0_\xd4\x8c\xc7\xb6\xf4Q\x96\xc8s\xc3j}1j\xf3\x0c[\xa4\xf8\xf5w\xe6\x9e\xd3\x11H\xe9
# \x1a\xf2B^[\xd2\xab`/:v[i\xc7\xfa\xa5k\xffN\x9a\xe7\xf5\x1c\x8eF\x8ca\rygH\xcf\xeb\xb2\x9b\x0e\xef\xf1\xeb"\xeew;U
# \x16\xc06\x98\xc2\xfeJ\xb2\xdfY\xaemu\x8aZL\x16U\xb6H\xcb\x16\x81IL\xc9\xe9\xef\xc3\x15\xf7\xed\xc4~"R\xd39$\xd5\x92
# \xa5j|/\xa5\xa1>\x0b\x10m\xac\x8d\x18*(\x1f\xb0\xe3Z\x82(\x9a\xa5\x05f\x1b\xb0\x8b\x0e2\x06\xcbtRB\xd0\x9c"\xdex\xda.
# \xd9?\xbe\xa8#\xe64\xadh\x9e\x0f\x83\x96OpX\xd7\xb2\x07\x85\\>nR\x83\xa5\xd3\xc9\xafW\x9a\xe6\xa6b]\x9a1\xd1W-\x86\xe6
# \x83\xf7\x98\xf6r\xf6Z\x19\xf3j/\xe7\xe0H\xa2\x98\\}\x0c`E\x140\xe2\xa6\x15P1\xc8\xe0\xc6\xaa0\xe0\xf3R$\xa3\xad\xc7
# m&\xd9x\x94\x04\x1a\xddC\x8b\xebN\xea"\xa5\x1a\xc3\\4\xdfKl\x14\xb7\xef>\x82\x8b\xfa\xc6;oN\xf0V\x9dq\xaf\x0f\x98l
# \xf1\xf0L\xca\x80er0YA\x80\x8c\xe4\xf79:\x8b\x81^n\x8e\x94\xc8\xf0\xda?\xb9,}\xc7'



# %%
# chiffrement fichier hello.py ayant comme contenu :
# import sys
#
# sys.path.append(r"D:\OneDrive\Documents\[PYTHON_PROJECTS]\corelibs")
#
# import corelibs
#
# dict = {"msg": "Hello Kim", "from": "papa"}
# print("message encapsulé chiffré", "•"*31, dict, dict["msg"], corelibs)
#
# corelibs.lazy.merge_dictionaries(dict, {"from": "papa ❤ =}"})

tools.cipher(r"D:\OneDrive\Documents\_TEST_\Chiffrements\hello.py", cle_secrete)
# résultat fichier binaire hello.py.clk
# 78da 1dc1 d7b2 4340 0000 d02f 32a3 2578
# d4a2 5bbb ba17 838d 1021 ca92 c8d7 df99
# 7b8e 248d 549c 5626 57fe 185a 7cda 4495
# 9e40 9695 6e51 e57f 86ec a79d 6467 218c
# edbc a60f 1435 8cb3 3fb9 a00a f5fc 9bc7
# db39 8aa2 d57c b6f3 048f c37b 247b 4aa6
# caaf 2395 f5ea 6860 f05a 7cb2 340d e201
# 91bd 2ec3 0dc4 fac8 8869 feba 6514 adcd
# eb1b 6a32 d117 804f 4e85 7a85 55f2 3528
# 67be 7b2d 6865 0e99 a477 3f38 4d0a dd36
# 33c2 ac0d 9f2f a4f7 5bb3 9892 aeba 46ef
# 8463 d374 f629 988e f8f0 8b67 3c13 e4be
# 32a3 5751 0d0b adf6 224f 99dd eb56 7a74
# b8fa b627 16e7 ea76 dc46 e8a3 93c6 1b5f
# 840f c752 5f7e b107 0ebf beee da06 58c4
# 29f0 80f5 e03c 0ac1 d2af b842 c58e 2e7d
# 27a7 1cbf 2b9e 2318 7cce 2a31 389c 5037
# 0719 013e b392 66d1 0f84 27d6 9312 4ac1
# c3f4 a0ac 6953 41e3 f6e5 c5ce 220c e331
# 58f9 1249 46df abbf 1b63 3db9 9a2f e470
# d835 e31b 4c65 d802 fffd 4bbe 356d e5ec
# e74e 0667 ab58 dfcc fc7e 9d93 df8f 7c38
# 4bde bb49 a305 0c47 1c04 b895 3621 72cc
# ea92 2c3a 7f0f 07dd 0b02 b7ed 6f2f 5abc
# 4001 d516 0876 a296 62d8 81e5 d298 8a30
# 0f50 974f a7dc 9db2 bd45 cd1f c744 a9e7

corelibs.tools.decipher(obj, password, delete_source_file=True)

Description

Permet de déchiffrer une chaîne de caractère ou un fichier

Warning

Le mot de passe tient compte de la casse.
Parameters
  • obj – indique la chaine de caractère ou le chemin absolu du fichier à déchiffrer

  • password – indique le mot de passe à appliquer lors du déchiffrement

  • delete_source_file

    indique si le fichier source doit être supprimé après le déchiffrement

    valeurs possibles: False/True
    valeur par défaut: True

Returns

True
ou
chaîne déchiffrée (si obj est une chaîne binaire)

Exemple :

# decipher.py
# %%
from corelibs import tools, lazy as lz

cle_secrete = "Clé secrète de la morkitu! • 28Nov2013KT"

# %%
# déchiffrement chaine de caractères
message = """
import corelibs

dict = {"msg": "Hello Kim", "from": "papa"}
print("message encapsulé chiffré", "•"*31, dict, dict["msg"], corelibs)

corelibs.lazy.merge_dictionaries(dict, {"from": "papa ❤ =}"})
"""

message_chiffre = tools.cipher(message, cle_secrete)

message_dechiffre = tools.decipher(message_chiffre, cle_secrete)
print(message_dechiffre)
# affiche
# import corelibs
#
# dict = {"msg": "Hello Kim", "from": "papa"}
# print("message encapsulé chiffré", "•"*31, dict, dict["msg"], corelibs)
#
# corelibs.lazy.merge_dictionaries(dict, {"from": "papa ❤ =}"})

# %%
# exécution dynamique de la chaîne déchiffrée
exec(message_dechiffre)  # affichera le print chiffré => message encapsulé chiffré ••••••••••••••••••••••••••••••• {'msg': 'Hello Kim', 'from': 'papa'} Hello Kim <module 'corelibs' from 'D:\\OneDrive\\Documents\\[PYTHON_PROJECTS]\\corelibs\\corelibs\\__init__.py'>

print("dictionnaire défini dans la chaine déchiffrée : ", dict)  # dictionnaire défini dans la chaine déchiffrée :  {'msg': 'Hello Kim', 'from': 'papa ❤ =}'}
# note :
# le dictionnaire initiale est {"msg": "Hello Kim", "from": "papa"}
# mais la valeur a été écrasée par l'instruction corelibs.lazy.merge_dictionaries(dict, {"from": "papa ❤ =}"})
# ce qui donne bien
# dictionnaire défini dans la chaine déchiffrée :  {'msg': 'Hello Kim', 'from': 'papa ❤ =}'}


# %%
# déchiffrement fichier .clk
tools.decipher(r"D:\OneDrive\Documents\_TEST_\Chiffrements\hello.py.clk", cle_secrete)

# %%
# exécution du fichier déchiffré 1ère version
lz.add_dir_path_2_project(r"D:\OneDrive\Documents\_TEST_\Chiffrements")
import hello  # charge le module hello.py déchiffré
print("*"*31, hello.dict)  # affichera ******************************* {'msg': 'Hello Kim', 'from': 'papa ❤ =}'}

# %%
# ou si les chemins sont bien définis, 2ème version
exec(open(r"D:\OneDrive\Documents\_TEST_\Chiffrements\hello.py", encoding="utf-8").read())
import hello as lo  # charge le module hello.py déchiffré
print("•"*31, lo.dict)  # affichera ••••••••••••••••••••••••••••••• {'msg': 'Hello Kim', 'from': 'papa ❤ =}'}
# attention, la 2ème version fonctionne car dans le programme hello.py, il y a une instruction sys.path.append(...)
corelibs.tools.get_file_properties(file_path, pretty_byte_size=True, ignore_errors=False)

Description

Récupère les informations d’un fichier ou d’un répertoire
Parameters
  • file_path – chemin absolu du fichier/répertoire

  • pretty_byte_size

    afficher la taille du fichier/répertoire de manière lisible pour un humain

    valeurs possibles: False/True
    valeur par défaut: True

  • ignore_errors

    forcer l’exécution lorsqu’une erreur est levée

    valeurs possibles: False/True
    valeur par défaut: False (cf. DEFAULT_IGNORE_ERROR dans Installation & Mise à jour)

Returns

tuple nommé avec comme attributs :
  • st_mode (mode de protection en binaire)

  • st_ino (n° inode)

  • st_dev (n° machine)

  • st_nlink (nb de liens)

  • st_uid (propriétaire)

  • st_gid (groupe id sous Windows ou groupe sous Unix)

  • st_size (taille du fichier/répertoire)

  • st_atime (date dernier accès)

  • st_mtime (date dernière modification)

  • st_ctime (date de création sous Windows et date dernier modif/accès sous Unix)

Exemple :

# get_file_properties.py
# %%
from corelibs import tools as to


# %%
# récupération des informations par défaut (affichange la taille dynamiquement, à la valeur la plus appropriée)
filename = r"D:\OneDrive\Documents\_TEST_\2020-11-11.jpg"
filename_properties = to.get_file_properties(filename)
print(filename_properties)
# affiche l'objet FileProperties(
#   st_mode=33206,
#   st_ino=11540474045256220,
#   st_dev=3199390331,
#   st_nlink=1,
#   st_uid='Invités',
#   st_gid=0,
#   st_size='96.26 Ko',  # la taille est exprimée en Ko pour une petite image
#   st_atime='13/11/2020 22:22:21',
#   st_mtime='11/11/2020 22:10:46',
#   st_ctime='11/11/2020 22:10:39')

# %%
# récupération des informations par défaut (affichange la taille dynamiquement, à la valeur la plus appropriée)
filename = r"D:\OneDrive\Documents\_TEST_\Zatoichi.avi"
filename_properties = to.get_file_properties(filename)
print(filename_properties)
# affiche l'objet FileProperties(
#   st_mode=33206,
#   st_ino=1125899906961926,
#   st_dev=3199390331,
#   st_nlink=1, st_uid='miche',
#   st_gid=0,
#   st_size='699.78 Mo',  # la taille est exprimée en Mo pour le film Zatoichi.avi
#   st_atime='13/11/2020 22:19:27',
#   st_mtime='22/03/2013 20:17:02',
#   st_ctime='10/11/2020 15:56:52')

# %%
# récupération des informations avec la taille ventilée par unité de mesure (jusqu'au To)
# fichier plus gros
filename = r"D:\OneDrive\Documents\_TEST_\Zatoichi.avi"
filename_properties = to.get_file_properties(filename, pretty_byte_size=False)
print(filename_properties)
# affiche l'objet FileProperties(
#   st_mode=33206,
#   st_ino=1125899906961926,
#   st_dev=3199390331,
#   st_nlink=1,
#   st_uid='miche',
#   st_gid=0,
#   st_size=ByteSize(  # objet ByteSize contenant toutes les tailles converties depuis le nb d'octets
#       byte=733775872,
#       kilobyte=716578.0,
#       megabyte=699.78,
#       gigabyte=0.68,
#       terabyte=0.0),
#   st_atime='13/11/2020 22:19:27',
#   st_mtime='22/03/2013 20:17:02',
#   st_ctime='10/11/2020 15:56:52')
# pour accéder à la taille en gigaoctet (gigabyte) par exemple récupérer directement son attribut
print(
    "La faille en Go du fichier Zatoichi.avi est de {taille} Go"
    .format(taille=filename_properties.st_size.gigabyte)
)  # affichera "La faille en Go du fichier Zatoichi.avi est de 0.68 Go"

# %%
# récupération des informations par défaut (affichange la taille dynamiquement, à la valeur la plus appropriée)
filename = r"D:\OneDrive\Documents\_TEST_"
filename_properties = to.get_file_properties(filename)
print(filename_properties)
# affiche l'objet FileProperties(
#   st_mode=16895,
#   st_ino=281474976804077,
#   st_dev=3199390331,
#   st_nlink=1,
#   st_uid='miche',
#   st_gid=0,
#   st_size='4.12 Go',  # la taille est exprimée en Go pour le répertoire _TEST_
#   st_atime='14/11/2020 18:23:11',
#   st_mtime='14/11/2020 17:58:08',
#   st_ctime='07/10/2020 22:17:12')
corelibs.tools.get_fingerprint(obj, algorithm='sha256', eval_as_string=False, chunk=67108864, ignore_errors=False)

Description

Calculer l’empreinte digitale (signature numérique) d’une chaîne de caractères ou d’un fichier passé en argument
Parameters
  • obj

    chaîne de caractères
    ou
    chemin absolu du fichier

  • algorithm

    applique l’algorithme de hashage

    valeurs possibles: ‘blake2b’, ‘blake2s’, ‘md5’, ‘sha1’, ‘sha224’, ‘sha256’, ‘sha384’, ‘sha512’, ‘sha3_256’, ‘sha3_224’, ‘sha3_384’ ou ‘sha3_512’
    valeur par défaut: sha256

  • eval_as_string

    permet de forcer l’évaluation comme étant une chaine de caractère (si l’objet passé en argument a un nom contenant des caractères \ ou / et n’est pas en réalité un chemin de fichiers)

    valeurs possibles: False/True
    valeur par défaut: False

  • chunk

    indique le buffer de lecture.

    valeur par défaut: DEFAULT_BYTE_CHUNK_SIZE (cf. Installation & Mise à jour)

  • ignore_errors

    forcer l’exécution lorsqu’une erreur est levée

    valeurs possibles: False/True
    valeur par défaut: False (cf. DEFAULT_IGNORE_ERROR dans Installation & Mise à jour)

Returns

empreinte digital unique

Exemple :

# get_fingerprint.py
# %%
from corelibs import tools as to, log


# %%
# Même empreinte = Même fichier (quelque soit son nom, sa date de modif, propriétaire etc...)
# fichier 1
filename = r"D:\OneDrive\Documents\_TEST_\2020-11-11.jpg"
print(to.get_file_properties(filename))  # FileProperties(st_mode=33206, st_ino=11540474045256220, st_dev=3199390331, st_nlink=1, st_uid='Invités', st_gid=0, st_size='96.26 Ko', st_atime='14/11/2020 22:12:45', st_mtime='11/11/2020 22:10:46', st_ctime='11/11/2020 22:10:39')
print(to.get_fingerprint(filename))  # b6bbd28aebe109adda9bc16a8f838a5043d7980968a37a5d48f890f7fb4d89dc

# %%
# fichier 1 copié et les propriétés affichent bien une différence
copy_filename = r"D:\OneDrive\Documents\_TEST_\2020-11-11 - Copie.jpg"
print(to.get_file_properties(copy_filename))  # FileProperties(st_mode=33206, st_ino=19984723346576074, st_dev=3199390331, st_nlink=1, st_uid='miche', st_gid=0, st_size='96.26 Ko', st_atime='14/11/2020 22:06:59', st_mtime='11/11/2020 22:10:46', st_ctime='14/11/2020 21:56:29')
print(to.get_fingerprint(copy_filename))  # b6bbd28aebe109adda9bc16a8f838a5043d7980968a37a5d48f890f7fb4d89dc

# %%
# algorithme disponible pour le hashage
print(to.get_fingerprint(filename, algorithm="blake2b"))  # 0a0ba79c2e6535b256c110bf823ea21b32fa7d1fdbeb16374ec3437269ad1f01e4dfb6a72b0dab929807737a331ac6a8988ebb6943d4ef10e5650128f4ac0ef0
print(to.get_fingerprint(filename, algorithm="blake2s"))  # 0c6b739b821f0fb560e5545445e3a4c600ffc171e506ff307be0528f9d274b56
print(to.get_fingerprint(filename, algorithm="md5"))  # 62383435cc0519bad598cc51783c5d47
print(to.get_fingerprint(filename, algorithm="sha1"))  # f50a38a0c0952f96ae98aa54da7929bfb463f8dd
print(to.get_fingerprint(filename, algorithm="sha224"))  # c176d2d98a4f8563762ce3863efb942dbb9c496b4bee89a0374e42fa
print(to.get_fingerprint(filename, algorithm="sha256"))  # b6bbd28aebe109adda9bc16a8f838a5043d7980968a37a5d48f890f7fb4d89dc
print(to.get_fingerprint(filename, algorithm="sha384"))  # c839a0c58ef1ba3a7577de093f3d0e3746600b7b28fa08881f62aa7d4871220b9373ebdc620704e84c965d972cf887fb
print(to.get_fingerprint(filename, algorithm="sha512"))  # 39e72a89e167aa86f998e61763b0b43619cb6347118dd5f6713f830688b7e16cc8b006e8027a3bf1b0c260d24a76a13e7e233c3ab179f4e76865215a1d4007a4
print(to.get_fingerprint(filename, algorithm="sha3_256"))  # e0009df152ee03665a3f26bc434d143873f2abc900e85e4e45d85608d6fd1787
print(to.get_fingerprint(filename, algorithm="sha3_224"))  # 0f6ab25b4406ed8f98decae73375451286166d2e3d01095c15530069
print(to.get_fingerprint(filename, algorithm="sha3_384"))  # 3d3d00f27d1663273d80cd9d4f1f55cf2f185d12f7c4387a77e91daae5ff179ecf8692c975b2f86b92010de5202ce32a
print(to.get_fingerprint(filename, algorithm="sha3_512"))  # ec208132962c717b6f92105ff04a2af93e2195fca22babc17b2b5ddb784b05c913b86cf09dd37e4ec699e342056051f61f400f290e7b8d5f4e8756d08d6e87ff

# %%
# Empreinte d'une chaine de caractère
str_2_hash = "Hello Kim!"
print(
    "L'empreinte SH256 de \"{str_2_hash}\" est \"{str_hashed}\""
    .format(
        str_2_hash=str_2_hash,
        str_hashed=to.get_fingerprint(str_2_hash)
    )
)  # L'empreinte SH256 de "Hello Kim!" est "8c9affc6a8329ea9c8a87cfe989565c21b24136dc57ccf9407aeefbcac87f97a"

# %%
str_2_hash = "/!\\ ATTENTION /!\\"  # doit être forcé comme chaine de caractères pour l'évaluation car contient des caractères \ et /
print(
    "L'empreinte SH256 de \"{str_2_hash}\" est \"{str_hashed}\""
    .format(
        str_2_hash=str_2_hash,
        str_hashed=to.get_fingerprint(str_2_hash, eval_as_string=True)
    )
)  # L'empreinte SH256 de "/!\ ATTENTION /!\" est "20f2328fcc5d513e330f5badadac2c0bd2685d8a21ce49daf7690cc6e783ff65"


# %%
# stress test sur un fichier des établissements d'open data gouv.fr
# taille fichier : 29 928 195 lignes, pour un poids total de 4.88 Go
@log.timing()
@log.status_bar()
def stress_test():
    str_2_hash = r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8.csv"
    print(
        "L'empreinte SH256 de \"{str_2_hash}\" est \"{str_hashed}\""
        .format(
            str_2_hash=str_2_hash,
            str_hashed=to.get_fingerprint(str_2_hash)
        )
    )


stress_test()  # Durée exécution : 00:00:12.07
# L'empreinte SH256 de "D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8.csv" est "072c0f2dc85bd326197318546da4331e6c900c39302145fc7013532aaedf4af8"
corelibs.tools.get_total_lines_in_file(file, chunk=67108864)

Description

Permet de compter le nombre de lignes dans un fichier plat
Parameters
  • file – indique l’emplacement du fichier avec son chemin absolu

  • chunk

    indique le buffer de lecture.

    valeur par défaut: DEFAULT_BYTE_CHUNK_SIZE (cf. Installation & Mise à jour)

Returns

total lignes lues

Exemple :

# get_total_lines_in_file.py
from corelibs import tools as to, log

print(
    "Total lignes lues :",
    to.get_total_lines_in_file(r"\\wsl$\Ubuntu-20.04\root\.zsh_history")
)  # affiche Total lignes lues : 46

print(
    "Total lignes lues :",
    to.get_total_lines_in_file(r"D:\OneDrive\Documents\_TEST_\SAS\Librairie_SAS\00_LIB_Macros_Communes.sas")
)  # affiche Total lignes lues : 4060


# stress test sur un fichier des établissements d'open data gouv.fr
# taille fichier : 29 928 195 lignes, pour un poids total de 4.88 Go
@log.timing()
@log.status_bar()
def stress_test():
    print(
        "Total lignes lues :",
        to.get_total_lines_in_file(r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8.tsv")
    )


stress_test()  # Durée exécution : 00:00:12.07
# Total lignes lues : 29928195
corelibs.tools.get_total_lines_in_folder(dir_2_scan, files_pattern, to_exclude=None)

Description

Permet de compter le total de nombres de lignes de tous les fichiers plats dans un dossier, avec possibilités de filtrage
Parameters
  • dir_2_scan – indique l’emplacement du répertoire à scanner en chemin absolu

  • files_pattern – indique le schéma du scan.

  • to_exclude – indique les exclusions, qu’elles soient des sous dossiers et/ou des fichiers. Les exclusions peuvent être des schémas d’exclusions.

Returns

tuple nommé avec comme attributs :
  • total_files (total de fichiers lus)

  • total_lines (total de lignes lues)

Exemple :

# get_total_lines_in_folder.py
# %%
from corelibs import tools as to

# %%
# Scan standard...
wcl = to.get_total_lines_in_folder(r"\\wsl$\Ubuntu-20.04\root", "*history")
print(wcl)  # affiche un objet Wcl(total_files=3, total_lines=431)

# %%
########################################################################################################################

# Scan avec multiple schéma et exclusion répertoires et/ou fichiers
# étalon *.sas
wcl = to.get_total_lines_in_folder(
    dir_2_scan=r"D:\OneDrive\Documents\_TEST_\SAS_ADD",
    files_pattern="*.sas"
)
print(wcl)  # affiche Wcl(total_files=95, total_lines=20943)
#
# étalon *.log
wcl = to.get_total_lines_in_folder(
    dir_2_scan=r"D:\OneDrive\Documents\_TEST_\SAS_ADD",
    files_pattern="*.log"
)
print(wcl)  # affiche Wcl(total_files=3, total_lines=587)
#
# scan multiple
wcl = to.get_total_lines_in_folder(
    dir_2_scan=r"D:\OneDrive\Documents\_TEST_\SAS_ADD",
    files_pattern=("*.sas", "*.log")
)
print(wcl)  # affiche Wcl(total_files=98, total_lines=21530) qui est bien le total des 2 sous ensembles étalons =)

# %%
########################################################################################################################

# Scan multiple avec exclusions
# étalon 1
wcl = to.get_total_lines_in_folder(
    dir_2_scan=r"D:\OneDrive\Documents\_TEST_\SAS_ADD\_QUAL_",
    files_pattern=("*.sas", "*.log")
)
print(wcl)  # Wcl(total_files=93, total_lines=20869)

# %%
# étalon 2
wcl = to.get_total_lines_in_folder(
    dir_2_scan=r"D:\OneDrive\Documents\_TEST_\SAS_ADD\[ _BACKUP_POINT_0 ]",
    files_pattern=("*.sas", "*.log")
)
print(wcl)  # Wcl(total_files=4, total_lines=654)

# %%
# étalon 3
print(
    "Total lignes lues :",
    to.get_total_lines_in_file(r"D:\OneDrive\Documents\_TEST_\SAS_ADD\hello.sas")
)  # Total lignes lues : 7

# %%
# étalon 4
wcl = to.get_total_lines_in_folder(
    dir_2_scan=r"D:\OneDrive\Documents\_TEST_\SAS_ADD",
    files_pattern=("*.sas", "*.log")
)
print(wcl)  # Wcl(total_files=98, total_lines=21530)
# 98 = 93 + 4 + 1
# 21530 = 20869 + 654 + 7

# %%
# exclusions étalon
wcl = to.get_total_lines_in_folder(
    dir_2_scan=r"D:\OneDrive\Documents\_TEST_\SAS_ADD",
    files_pattern=("*.sas", "*.log"),
    to_exclude=r"*_BACKUP_POINT_0*"
)
print(wcl)  # Wcl(total_files=94, total_lines=20876)
# 94 = 98 - 4
# 20876 = 21530 - 654

# %%
# exclusions finales
wcl = to.get_total_lines_in_folder(
    dir_2_scan=r"D:\OneDrive\Documents\_TEST_\SAS_ADD",
    files_pattern=("*.sas", "*.log"),
    to_exclude=(r"*_BACKUP_POINT_0*", r"*SAS_ADD\hello.sas")
)
print(wcl)  # Wcl(total_files=93, total_lines=20869)
# 93 = 98 - 4 - 1
# 20869 = 21530 - 654 - 7

# le compte y est! =}
corelibs.tools.scan_dir(dir_path, duplicated_files_indicator=False, skip_directories_properties=True, skip_pre_scan=False, ref_scan_file=None, caching=True, render='Excel', encoding='utf-8', std_print=False, gui_instance=None, force_excel_2_refresh=False)

Description

Permet de scanner un disque ou répertoire afin d’analyser son contenu, et en particulier d’y détecter des fichiers en doublon. Le scan s’appuie sur un caching pour réduire les temps de calcul et fonctionnera donc en mode delta à partir du 2ème scan.
Parameters
  • dir_path – indique chemin à analyser

  • duplicated_files_indicator

    indique s’il faut identifier les fichiers en doublon

    valeurs possibles: False/True
    valeur par défaut: False

  • skip_directories_properties

    indique s’il faut éviter le calcul des propriétés dossiers (pouvant être coûteux en temps d’exécution)

    valeurs possibles: False/True
    valeur par défaut: True

  • skip_pre_scan

    indique s’il faut éviter le préscan. Lorsqu’il est à True, alors le ref_scan_file sera pris en compte. Si ce dernier est à None, le cache sera utilisé (si existe), sinon une alerte sera levée. Et il faudrait donc préciser l’emplacement d’un fichier contenant les données scannées.

    valeurs possibles: False/True
    valeur par défaut: False

  • ref_scan_file

    indique le fichier référentiel du préscan. Si la valeur est à None alors le cache du préscan sera pris en compte si existe. Sinon le fichier préscan fournit en entrée doit avoir les conditions suivantes :

    • être un fichier CSV, séparé par ;

    • avoir l’entête suivante “Technical ID”;”File Type”;”File Name”;”Owner”;”Last Access”;”Last Modification”;”OS Time”;”File Size”;”Bytes”;”KiloBytes”;”MegaBytes”;”GigaBytes”;”Fingerprint”;”Is Duplicated”

    • assurer l’unicité du champ “Technical ID”

  • caching

    indique s’il faut utiliser ou non le caching

    valeurs possibles: False/True
    valeur par défaut: True

  • render

    indique le rendu final

    valeurs possibles: Excel, Web ou App
    valeur par défaut: Excel

  • encoding – indique l’encodage de lecture/écriture

  • std_print

    indique s’il faut utiliser la sortie standard pour le printing

    valeurs possibles: False/True
    valeur par défaut: False

  • gui_instance – indique l’instance gui (pour l’interface visuelle, non utile en instructions codes)

  • force_excel_2_refresh

    indique s’il faut forcer le rafraichissement des fichiers caches Excel

    valeurs possibles: False/True
    valeur par défaut: False

Returns

rien…

Warning

Le rendu “Web” n’est possible que sous Jupyter ou iPYthon

Note

A partir du 2ème scan, si le calcul des doublons est souhaité, les fichiers du préscan ne se trouvant pas dans le cache seront calculés. Si les fichiers du préscan se trouvent dans le cache, alors le calcul se rafraichit dans les cas suivants :
  • la taille du fichier a été modifiée

  • la date de modification a été changée

Exemple :

# scan_dir.py
# %%
from corelibs import tools

# scan initial
tools.scan_dir(r"D:\OneDrive\Documents\_TEST_\__LOGS__ - Copie", duplicated_files_indicator=True)

# affichage sans recalcul (si cache existe)
tools.scan_dir(r"D:\OneDrive\Documents\_TEST_\__LOGS__ - Copie", skip_pre_scan=True, render="Excel")

# scan delta
# à partir de maintenant, le dossier "D:\OneDrive\Documents\_TEST_\__LOGS__ - Copie" existe en cache et seuls les deltas
# détectés seront calculés...
tools.scan_dir(r"D:\OneDrive\Documents\_TEST_\__LOGS__ - Copie", duplicated_files_indicator=True)
_images/scan_dir.png

Scan via code

_images/scan_dir_gui.png

Scan via interface

class corelibs.tools.Archive
unzip(yaml_file=None, archive_name=None, files_2_unzip=None, ignore_errors=False)

Description

Permet de décompresser les données
Parameters
  • yaml_file – fichier de configuration pour décompresser ; si utilisé en argument, les 2 arguments archive_name et files_2_unzip ne seront pas pris en compte. (ce fichier peut être créé manuellement ou être celui généré par la méthode corelibs.tools.Archive.zip())

  • archive_name – nom du fichier archive (avec son ou ses extensions) contenant les fichiers à décompresser avec son chemin absolu (i.e. “D:\OneDrive\Documents\_TEST_\NOM_ARCHIVE.7z”)

  • files_2_unzip

    le ou les fichiers à décompresser

    si fichier simple, alors définir avec une chaîne de caractères
    si liste de fichiers alors définir les différentes chaînes de caractères dans un tuple, séparé par des virgules (i.e. (“fichier 1”, “fichier 2”, …, “fichier n”))
    les noms des fichiers peuvent être précédés par un chemin ou non :
    • sans le chemin en préfixe, alors l’emplacement du fichier à décompresser est celui du fichier archive

    • sinon le chemin en préfixe sera pris en priorité sur le chemin de l’archive pour la décompression

  • ignore_errors

    forcer l’exécution lorsqu’une erreur est levée

    valeurs possibles: False/True
    valeur par défaut: False (cf. DEFAULT_IGNORE_ERROR dans Installation & Mise à jour)

Returns

code retour :
  • 0 si OK

  • <> 0 si KO

Exemple :

# unarchive.py
from corelibs import tools as to


archive = to.Archive()
# Décompression via un fichier de configuration YAML
exit_code = archive.unzip(yaml_file=r"D:\OneDrive\Documents\_TEST_\TEST.yaml")

# Décompression manuelle
exit_code = archive.unzip(
    archive_name=r"D:\OneDrive\Documents\_TEST_\\/TEST.28112013.7z",  # pas beau, mais on peut rentrer ce que l'on veut
    files_2_unzip=(
        #  fichiers décompressés à la racine de l'archive (i.e. "D:\OneDrive\Documents\_TEST_\\/")
        r"D:\OneDrive\Desktop\1-01 Act One Questo Mar Rosso.m4a",
        "_éèçàoöôîïêëùûü;.txt",
        # fichier décompressé à un emplacement spécifique, ici "D:\OneDrive\Documents\_TEST_\A zip"
        r"D:\OneDrive\Documents\_TEST_\dossier compressé"
    )
)
zip(yaml_file=None, archive_name=None, files_2_zip=None, delete_sources_files=False, ignore_errors=False)

Description

Permet de compresser les données
Parameters
  • yaml_file

    fichier de configuration pour compresser ; si utilisé en argument, les 2 arguments archive_name et files_2_zip ne seront pas pris en compte.

    Un fichier de configuration ayant pour nom “archive_name.YAML” sera généré à la racine de archive_name et peut être utilisé en argument pour :

  • archive_name – nom souhaité pour l’archive avec son chemin absolu pour le stockage (i.e. “D:\OneDrive\Documents\_TEST_\NOM_ARCHIVE”)

  • files_2_zip

    le ou les fichiers à compresser

    si fichier simple, alors définir avec une chaîne de caractères
    si liste de fichiers alors définir les différentes chaînes de caractères dans un tuple, séparé par des virgules (i.e. (“fichier 1”, “fichier 2”, …, “fichier n”))
    les noms des fichiers peuvent être précédés par un chemin ou non :
    • sans le chemin en préfixe, alors l’emplacement du fichier à ziper est celui du fichier archive

    • sinon le chemin en préfixe sera pris en priorité sur le chemin de l’archive

  • delete_sources_files

    supprimer les fichiers sources une fois la compression finie (i.e. terminée sans erreur ou sans avoir été arrêtée par un autre processus)

    valeurs possibles: False/True
    valeur par défaut: False

  • ignore_errors

    forcer l’exécution lorsqu’une erreur est levée

    valeurs possibles: False/True
    valeur par défaut: False (cf. DEFAULT_IGNORE_ERROR dans Installation & Mise à jour)

Returns

code retour :
  • 0 si OK

  • <> 0 si KO

Note

fichier s’entend au sens Unix du terme, i.e. fichier régulier ou répertoire

Exemple :

# archive.py
from corelibs import lazy as lz, tools as to


archive = to.Archive()
# Compression via un fichier de configuration YAML
exit_code = archive.zip(yaml_file=r"D:/OneDrive//\\//Documents\_TEST_\TEST.yaml")  # pas beau, mais on peut rentrer ce que l'on veut...

# Compression manuelle
exit_code = archive.zip(
    archive_name=r"D:/OneDrive/Documents\_TEST_\MON_ARCHIVE_" + lz.get_timestamp(),
    files_2_zip=(
        #  fichiers présents à la racine de l'archive (i.e. "D:/OneDrive/Documents\_TEST_")
        "1-01 Act One Questo Mar Rosso.m4a",
        "_éèçàoöôîïêëùûü;.txt",
        # fichier à un emplacement spécifique, ici "D:\OneDrive\Documents\_TEST_\A zip"
        r"D:\OneDrive\Documents\_TEST_\A zip\A zip - Copie"
    )
)

# stress test sur un fichier des établissements d'open data gouv.fr
# taille fichier : 29 928 195 lignes, pour un poids total de 4.88 Go
archive.zip(
    archive_name=r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8" + lz.get_timestamp(),
    files_2_zip=r"D:\OneDrive\Desktop\StockEtablissement_utf8\StockEtablissement_utf8.csv"
)

Exemple Fichier YAML généré :

###########################################################################################################################
# Généré le mercredi 16 décembre 2020 à 23:19:58 ##########################################################################
###########################################################################################################################
archive:
  name: TEST.7z
  root: D:\OneDrive\Documents\_TEST_
files:
- 1-01 Act One Questo Mar Rosso.m4a
- _éèçàoöôîïêëùûü;.txt
- D:\OneDrive\Documents\_TEST_\A zip\A zip - Copie
###########################################################################################################################

Dépendances

Description générale

Les dépendances sont installées automatiquement lors de l’installation/MAJ du package corelibs.

Dans le cas d’une création manuelle d’un nouvel environnement virtuel, selon le contexte de création, il est peut-être nécessaire de réinstaller manuellement les dépendances ci-dessous dans le-dit nouvel environnement.

Note

Pour mémoire, l’installation d’un package se fait avec cette commande

$ pip install nom_package

Et la mise à jour d’un package se fait avec cette commande

$ pip install nom_package -U

Index & Tables des matières