Au final, je suis parti sur la bibliothèque PyWin32 pour manipuler en direct les fonctionnalités windows exploitées de toute façon par WatchDog. Le résultat est fonctionnel.
J'ai lancé mon Handler perso via un thread à part (pas sûr que ce soit forcément obligé).
EDIT : Pas besoin
J'ai été obligé de rallonger extrêmement la taille du buffer lors de la création de la routine afin que tous mes dossier.D copiés soient détectés avec l'exécution du handler (au début j'étais sur 10000, et à ce moment là même problème que auparavant : il ne répertoriait pas tous les dossiers copiés). C'est à priori un paramètre à grandir manuellement afin de pouvoir détecter les cas de multi-copies assez longues (voir ici et ici).
La raison dans mon cas est sûrement que lors de l'exécution de mon handler, d'autres fichiers sont créés sur C:\ ce qui remplit peu à peu le buffer et donc vire les anciennes instances (celles qui m'intéressent donc).
Voir ici pour une doc plutôt complète sur le fonctionnement de cette API Windows.
Basé sur le code de Tim Golden --> voir ici
#!/usr/bin/env python
import os
import sys
import win32file
import win32event
import win32con
import logging
import configparser
import time
import threading
# Création de la liste d'action.
# Dans ce cas, uniquement besoin des fichiers créés, mais autres cas possibles à ajouter
ACTIONS = {
1 : "Created",
}
# Dictionnaire résultat final
dict_log = {
'date' : '',
'chemin' : '',
'instrument' : '',
'methode' : '',
'sample' : ''
}
# Différentes fonctions pour le Handler perso
def TestData(path):
if path[-2:] == '.D' and not '$' in path:
result = True
else:
result = False
return result
def LectureRUNSTART(path):
file = path + '\\runstart.txt'
n = 1
while n < 15:
try:
file = open(file, 'r')
lines = file.readlines()
ilines = iter(lines)
for i in ilines:
if 'Sample Name' in i:
sample = i.split(' = ')
sample = sample[-1]
break
else:
sample = '<Sample Name non récupérable>'
n = 15
except FileNotFoundError:
n += 1
time.sleep(1)
if n == 15:
sample = '<Sample Name non récupérable>'
global dict_log
dict_log['sample'] = sample
return
def LectureINI(path):
file_ini = path + '\\PRE_POST.INI'
m = 1
while m < 15:
try:
cfg2 = configparser.ConfigParser()
cfg2.read(file_ini)
m = 15
except:
m += 1
time.sleep(1)
if m == 15:
date = '<Date non récupérable>'
instr = '<Nom Instrument non récupérable>'
method = '<Méthode non récupérable>'
sections = ['POSTRUN', 'POSTRUN', 'Sequence']
keys = ['date', 'instrument name', '_methfile$']
i = 0
while i < 3:
if (sections[i] not in cfg2.sections() or keys[i] not in cfg2.options(sections[i])):
if i == 0:
date = '<Date non récupérable>'
elif i == 1:
instr = '<Nom Instrument non récupérable>'
else:
method = '<Méthode non récupérable>'
else:
value = cfg2[sections[i]][keys[i]]
if i == 0:
date = value
elif i == 1:
instr = value
else:
method = value
i += 1
global dict_log
dict_log['date'] = date
dict_log['methode'] = method
dict_log['instrument'] = instr
return
def Extraction(full_filename):
logging.basicConfig(filename='C:\\WatchData\\WatchData.log',
level=logging.INFO,
format='%(asctime)s\t%(message)s',
datefmt='%Y-%m-%d %H:%M:%S')
LectureRUNSTART(full_filename)
LectureINI(full_filename)
dict_log['chemin'] = full_filename
infos = (dict_log['date'] + '\t' +
dict_log['chemin'] + '\t' +
dict_log['instrument'] + '\t' +
dict_log['methode'] + '\t' +
dict_log['sample'])
logging.info('%s', infos)
return
# Thanks to Claudio Grondi for the correct set of numbers
FILE_LIST_DIRECTORY = 0x0001
# Definition of path_to_watch
cfg = configparser.ConfigParser()
try:
cfg.read('C:\Windows\win.ini')
path_to_watch = cfg['WatchData']['Path']
path_to_watch = path_to_watch[:-1]
except:
path_to_watch = 'C:\\'
# Creating directory to watch
hDir = win32file.CreateFile (
path_to_watch,
FILE_LIST_DIRECTORY,
win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE | win32con.FILE_SHARE_DELETE,
None,
win32con.OPEN_EXISTING,
win32con.FILE_FLAG_BACKUP_SEMANTICS,
None
)
# Watching routine
while 1:
#
# ReadDirectoryChangesW takes a previously-created
# handle to a directory, a buffer size for results,
# a flag to indicate whether to watch subtrees and
# a filter of what changes to notify.
#
# NB Tim Juchcinski reports that he needed to up
# the buffer size to be sure of picking up all
# events when a large number of files were
# deleted at once.
#
results = win32file.ReadDirectoryChangesW (
hDir,
#Augmenter buffer si problème de détection simultanée
1000000,
True,
win32con.FILE_NOTIFY_CHANGE_FILE_NAME |
win32con.FILE_NOTIFY_CHANGE_DIR_NAME |
win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES |
win32con.FILE_NOTIFY_CHANGE_SIZE |
win32con.FILE_NOTIFY_CHANGE_LAST_WRITE |
win32con.FILE_NOTIFY_CHANGE_SECURITY,
None,
None
)
for action, file in results:
full_filename = os.path.join (path_to_watch, file)
if TestData(full_filename) == True and ACTIONS.get(action, "Unknown") == "Created":
print(full_filename, ACTIONS.get(action, "Unknown"))
#threading.Thread(target=Extraction(full_filename)).start()
Extraction(full_filename)